diff --git a/apps/desktop/e2e/quick-chat.spec.ts b/apps/desktop/e2e/quick-chat.spec.ts new file mode 100644 index 00000000000..95d94ef3f4d --- /dev/null +++ b/apps/desktop/e2e/quick-chat.spec.ts @@ -0,0 +1,376 @@ +import { mkdtempSync, writeFileSync } from 'node:fs' +import type { Server } from 'node:http' +import { createServer } from 'node:http' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import type { ElectronApplication, Page } from '@playwright/test' +import { _electron as electron, expect, test } from '@playwright/test' +import type { DesktopPreferences, QuickChatShortcut } from '@sim/desktop-bridge' +import { getErrorMessage } from '@sim/utils/errors' +import { recordCheck } from './check-report' + +/** + * Drives the quick chat window in the real shell against a fixture origin: where it opens, that + * it floats with no desktop bridge, that it resizes within its bounds, that "Open in Sim" hands + * the chat to a full window, that the toggle hides and restores the same page, that sign-out + * destroys it, and how its shortcut setting and load failures behave. Each check lands in a JSON + * report at QUICK_CHAT_REPORT_PATH as it finishes. + */ + +const DESKTOP_DIR = fileURLToPath(new URL('..', import.meta.url)) +const WORKSPACE_ROUTE = '/workspace/ws1/home' +const DEFAULT_SHORTCUT: QuickChatShortcut = 'Alt+Space' + +const PAGES: Record = { + [WORKSPACE_ROUTE]: '

workspace

', + '/extension/ws1': `

quick-chat

+ Open in Sim + `, + '/workspace/ws1/chat/c1': + '

handed-off

', + '/login': '

fixture-login

', +} + +interface FixtureServer { + server: Server + origin: string + /** Drops every connection for `path`, so its loads fail, until `failing` is turned off again. */ + setFailing: (path: string, failing: boolean) => void +} + +function startFixtureServer(): Promise { + const failing = new Set() + return new Promise((resolvePromise) => { + const server = createServer((request, response) => { + const path = new URL(request.url ?? '/', 'http://127.0.0.1').pathname + if (failing.has(path)) { + request.socket.destroy() + return + } + if (path === '/api/auth/sign-out') { + response.writeHead(200, { 'Content-Type': 'application/json' }).end('{}') + return + } + const body = PAGES[path] + if (!body) { + response.writeHead(404, { 'Content-Type': 'text/html' }).end('

not found

') + return + } + response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }).end(body) + }) + server.listen(0, '127.0.0.1', () => { + const address = server.address() + const port = typeof address === 'object' && address ? address.port : 0 + resolvePromise({ + server, + origin: `http://127.0.0.1:${port}`, + setFailing: (path, on) => { + if (on) failing.add(path) + else failing.delete(path) + }, + }) + }) + }) +} + +function launchApp(origin: string, settings: string | Record = {}) { + const userData = mkdtempSync(join(tmpdir(), 'sim-desktop-quick-chat-')) + writeFileSync( + join(userData, 'settings.json'), + typeof settings === 'string' + ? settings + : JSON.stringify({ origin, lastRoute: WORKSPACE_ROUTE, ...settings }) + ) + return electron.launch({ + args: ['.'], + cwd: DESKTOP_DIR, + env: { ...process.env, SIM_DESKTOP_ORIGIN: origin, SIM_DESKTOP_USER_DATA: userData }, + }) +} + +/** Clicks an application-menu item by label, the way a user choosing it from the menu bar would. */ +function clickMenu(app: ElectronApplication, label: string): Promise { + return app.evaluate(({ Menu }, label) => { + const find = (items: Electron.MenuItem[]): Electron.MenuItem | null => { + for (const item of items) { + if (item.label === label) return item + const found = item.submenu ? find(item.submenu.items) : null + if (found) return found + } + return null + } + const item = find(Menu.getApplicationMenu()?.items ?? []) + if (!item) throw new Error(`No menu item ${label}`) + item.click() + }, label) +} + +/** Native state of the window showing `url`, or null once no window shows it. */ +function windowState(app: ElectronApplication, url: string) { + return app.evaluate(({ BrowserWindow }, url) => { + const win = BrowserWindow.getAllWindows().find( + (candidate) => !candidate.isDestroyed() && candidate.webContents.getURL() === url + ) + return win + ? { + visible: win.isVisible(), + focused: win.isFocused(), + alwaysOnTop: win.isAlwaysOnTop(), + size: win.getSize(), + } + : null + }, url) +} + +/** Resizes the window showing `url` the way dragging its edge would. */ +function resizeWindow(app: ElectronApplication, url: string, width: number, height: number) { + return app.evaluate( + ({ BrowserWindow }, { url, width, height }) => { + BrowserWindow.getAllWindows() + .find((win) => win.webContents.getURL() === url) + ?.setSize(width, height) + }, + { url, width, height } + ) +} + +const hasBridge = (page: Page) => + page.evaluate(() => typeof (globalThis as { simDesktop?: unknown }).simDesktop !== 'undefined') + +/** Changes the shortcut through the settings bridge, as the desktop settings page does. */ +const setShortcut = (page: Page, shortcut: string | null) => + page.evaluate( + (value) => + ( + globalThis as unknown as { + simDesktop: { + settings: { setQuickChatShortcut(v: unknown): Promise } + } + } + ).simDesktop.settings.setQuickChatShortcut(value), + shortcut + ) + +const isRegistered = (app: ElectronApplication, shortcut: string) => + app.evaluate(({ globalShortcut }, value) => globalShortcut.isRegistered(value), shortcut) + +/** Runs one named check and records its outcome as soon as it is known. */ +async function check(name: string, body: () => Promise) { + const reportPath = process.env.QUICK_CHAT_REPORT_PATH ?? test.info().outputPath('quick-chat.json') + const started = Date.now() + const record = (status: 'passed' | 'failed', error?: unknown) => + recordCheck(reportPath, 'quick-chat', { + name, + status, + durationMs: Date.now() - started, + retry: test.info().retry, + ...(error === undefined ? {} : { error: getErrorMessage(error) }), + }) + try { + await body() + record('passed') + } catch (error) { + record('failed', error) + throw error + } +} + +test.describe('quick chat window', () => { + let fixture: FixtureServer + let app: ElectronApplication | undefined + + test.beforeAll(async () => { + fixture = await startFixtureServer() + }) + + test.afterAll(() => { + fixture.server.close() + }) + + test.afterEach(async () => { + await app?.close().catch(() => {}) + }) + + test('opens, resizes, hands off, toggles, and closes on sign-out', async () => { + const launched = await launchApp(fixture.origin) + app = launched + const main = await launched.firstWindow() + await expect(main.locator('#app')).toHaveText('workspace') + const quickUrl = `${fixture.origin}/extension/ws1` + let quick: Page | undefined + + await check('opens the compact chat for the last workspace as a floating window', async () => { + const opened = launched.waitForEvent('window') + await clickMenu(launched, 'Quick Chat') + quick = await opened + await expect(quick.locator('#quick')).toHaveText('quick-chat') + expect(quick.url()).toBe(quickUrl) + await expect + .poll(() => windowState(launched, quickUrl)) + .toMatchObject({ visible: true, alwaysOnTop: true }) + }) + + await check('the floating page gets no desktop bridge while full windows keep it', async () => { + if (!quick) throw new Error('Quick chat did not open') + expect(await hasBridge(quick)).toBe(false) + expect(await hasBridge(main)).toBe(true) + }) + + await check('resizing holds the minimum size and the page follows the window', async () => { + if (!quick) throw new Error('Quick chat did not open') + const page = quick + await resizeWindow(launched, quickUrl, 200, 200) + await expect.poll(() => windowState(launched, quickUrl)).toMatchObject({ size: [360, 420] }) + // Small enough for any runner display; macOS clamps a window to the screen it is on. + await resizeWindow(launched, quickUrl, 600, 560) + await expect.poll(() => windowState(launched, quickUrl)).toMatchObject({ size: [600, 560] }) + await expect.poll(() => page.evaluate(() => window.innerWidth)).toBe(600) + }) + + await check( + 'Open in Sim hands the chat to a full window and leaves the panel open', + async () => { + if (!quick) throw new Error('Quick chat did not open') + const handedOff = launched.waitForEvent('window') + await quick.locator('#open-in-sim').click() + const full = await handedOff + await expect(full.locator('#chat')).toHaveText('handed-off') + expect(await hasBridge(full)).toBe(true) + expect(await windowState(launched, quickUrl)).toMatchObject({ + visible: true, + alwaysOnTop: true, + }) + } + ) + + await check( + 'the toggle brings a background panel forward, then hides it, keeping the page', + async () => { + if (!quick) throw new Error('Quick chat did not open') + await quick.evaluate(() => { + ;(globalThis as { quickChatMarker?: string }).quickChatMarker = 'kept' + }) + // The hand-off focused the full window; the app must be active for window focus to land. + await launched.evaluate(({ app }) => app.focus({ steal: true })) + await clickMenu(launched, 'Quick Chat') + await expect + .poll(() => windowState(launched, quickUrl)) + .toMatchObject({ visible: true, focused: true }) + await clickMenu(launched, 'Quick Chat') + await expect.poll(() => windowState(launched, quickUrl)).toMatchObject({ visible: false }) + await clickMenu(launched, 'Quick Chat') + await expect.poll(() => windowState(launched, quickUrl)).toMatchObject({ visible: true }) + expect( + await quick.evaluate(() => (globalThis as { quickChatMarker?: string }).quickChatMarker) + ).toBe('kept') + } + ) + + await check('signing out destroys the panel with the account it belonged to', async () => { + await clickMenu(launched, 'Sign Out') + // Found as the only always-on-top window, not by URL: sign-out sends full windows to /login. + await expect + .poll( + () => + launched.evaluate(({ BrowserWindow }) => + BrowserWindow.getAllWindows().some((win) => !win.isDestroyed() && win.isAlwaysOnTop()) + ), + { timeout: 30_000 } + ) + .toBe(false) + }) + }) + + test('the shortcut setting and a failed first load', async () => { + test.skip(process.platform !== 'darwin', 'Only macOS binds a shortcut by default') + const launched = await launchApp(fixture.origin, { quickChatShortcut: 'CommandOrControl+Q' }) + app = launched + const main = await launched.firstWindow() + await expect(main.locator('#app')).toHaveText('workspace') + + await check('a stored shortcut outside the presets falls back to the default', async () => { + const preferences = await main.evaluate(() => + ( + globalThis as unknown as { + simDesktop: { settings: { getPreferences(): Promise } } + } + ).simDesktop.settings.getPreferences() + ) + expect(preferences).toMatchObject({ + quickChatShortcut: DEFAULT_SHORTCUT, + quickChatShortcutAvailable: true, + }) + expect(await isRegistered(launched, DEFAULT_SHORTCUT)).toBe(true) + expect(await isRegistered(launched, 'CommandOrControl+Q')).toBe(false) + }) + + await check('choosing another shortcut releases the old one and binds the new', async () => { + const next: QuickChatShortcut = 'CommandOrControl+Shift+Space' + expect(await setShortcut(main, next)).toMatchObject({ + quickChatShortcut: next, + quickChatShortcutAvailable: true, + }) + expect(await isRegistered(launched, next)).toBe(true) + expect(await isRegistered(launched, DEFAULT_SHORTCUT)).toBe(false) + }) + + await check( + 'turning it off releases the shortcut and refuses values off the list', + async () => { + expect(await setShortcut(main, null)).toMatchObject({ + quickChatShortcut: null, + quickChatShortcutAvailable: true, + }) + expect(await isRegistered(launched, 'CommandOrControl+Shift+Space')).toBe(false) + expect(await setShortcut(main, 'CommandOrControl+Q')).toMatchObject({ + quickChatShortcut: null, + }) + expect(await isRegistered(launched, 'CommandOrControl+Q')).toBe(false) + } + ) + + await check('a failed first load reloads when the panel is shown again', async () => { + fixture.setFailing('/extension/ws1', true) + const opened = launched.waitForEvent('window') + await clickMenu(launched, 'Quick Chat') + const quick = await opened + await quick.waitForLoadState().catch(() => {}) + expect(await quick.locator('#quick').count()).toBe(0) + fixture.setFailing('/extension/ws1', false) + await launched.evaluate(({ app }) => app.focus({ steal: true })) + // Pressing the shortcut either hides a focused panel or shows it again; each show after the + // failure reloads, so a few presses must land on the loaded page. + await expect + .poll( + async () => { + if ((await quick.locator('#quick').count()) > 0) return true + await clickMenu(launched, 'Quick Chat') + return false + }, + { intervals: [1_000], timeout: 15_000 } + ) + .toBe(true) + await expect(quick.locator('#quick')).toHaveText('quick-chat') + }) + }) + + test('stays closed while account data cannot be stored', async () => { + // An unreadable settings file blocks persistence, which makes account data unavailable. + const launched = await launchApp(fixture.origin, '[]') + app = launched + await launched.firstWindow() + await check( + 'the shortcut opens nothing while the window could not hold an account', + async () => { + await clickMenu(launched, 'Quick Chat') + await new Promise((resolveDelay) => setTimeout(resolveDelay, 1_500)) + expect( + await launched.evaluate(({ BrowserWindow }) => + BrowserWindow.getAllWindows().some((win) => !win.isDestroyed() && win.isAlwaysOnTop()) + ) + ).toBe(false) + } + ) + }) +}) diff --git a/apps/desktop/native/computer-use/ComputerUse.swift b/apps/desktop/native/computer-use/ComputerUse.swift index 9f287c330a7..a723bb77532 100644 --- a/apps/desktop/native/computer-use/ComputerUse.swift +++ b/apps/desktop/native/computer-use/ComputerUse.swift @@ -170,7 +170,9 @@ func axRect(position rawPosition: CFTypeRef?, size rawSize: CFTypeRef?) -> CGRec let rawSize, CFGetTypeID(rawSize) == AXValueGetTypeID() else { return nil } var position = CGPoint.zero; var size = CGSize.zero guard AXValueGetValue(unsafeBitCast(rawPosition, to: AXValue.self), .cgPoint, &position), - AXValueGetValue(unsafeBitCast(rawSize, to: AXValue.self), .cgSize, &size) else { return nil } + AXValueGetValue(unsafeBitCast(rawSize, to: AXValue.self), .cgSize, &size), + // Some apps report hidden elements at infinity (Maps does once its search field has focus); such an element has no usable frame. + [position.x, position.y, size.width, size.height].allSatisfy(\.isFinite) else { return nil } return CGRect(origin: position, size: size) } /// Apps computer use never operates, lowercased: macOS security settings, password managers, and apps @@ -1067,7 +1069,11 @@ func nextLine() throws -> String? { func encodeReply(_ response: [String: Any]) throws -> Data { let limit = 16 * 1024 * 1024 func encode(_ value: [String: Any]) throws -> Data { - try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys]) + // JSONSerialization raises an Objective-C exception, which Swift cannot catch, on a NaN or infinite number. + guard JSONSerialization.isValidJSONObject(value) else { + throw ComputerError("reply_unencodable", "The reply held a value JSON cannot represent.") + } + return try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys]) } let original = try encode(response) if original.count <= limit { return original } @@ -1138,9 +1144,11 @@ struct Main { response = ["id": id, "error": details] } catch { response = ["id": id, "error": ["code": "request_failed", "message": String(String(describing: error).prefix(2000))]] } - if let data = try? encodeReply(response) { - FileHandle.standardOutput.write(data); FileHandle.standardOutput.write(Data([10])) - } + let data: Data? + do { data = try encodeReply(response) } catch let error as ComputerError { + data = try? encodeReply(["id": id, "error": ["code": error.code, "message": error.message]]) + } catch { data = nil } + if let data { FileHandle.standardOutput.write(data); FileHandle.standardOutput.write(Data([10])) } } } } diff --git a/apps/desktop/native/computer-use/README.md b/apps/desktop/native/computer-use/README.md index 31acd652422..1603675f8e1 100644 --- a/apps/desktop/native/computer-use/README.md +++ b/apps/desktop/native/computer-use/README.md @@ -22,7 +22,7 @@ SIGTERM/SIGINT sets a cancellation flag. Traversal, typing and drag loops check ## Tests and limitations -Compile pure native tests with `swiftc -parse-as-library -D COMPUTER_USE_TEST ComputerUse.swift tests/NativeTests.swift -o native-tests`. They cover multi-display negative origins, coordinate bounds/nonfinite input, shortcut modifiers, invalid key handling, input-sequence and batch validation, protected apps and cooperative cancellation. The live fixture also covers batches and click-to-focus for an editor that ignores Accessibility focus. Real status and app discovery outputs were validated against the generated worker Zod schema (156 apps, including 147 installed-only apps). +Compile pure native tests with `swiftc -parse-as-library -D COMPUTER_USE_TEST ComputerUse.swift tests/NativeTests.swift -o native-tests`. They cover multi-display negative origins, coordinate bounds/nonfinite input, elements reported at infinity and unencodable replies, shortcut modifiers, invalid key handling, input-sequence and batch validation, protected apps and cooperative cancellation. The live fixture also covers batches and click-to-focus for an editor that ignores Accessibility focus. Real status and app discovery outputs were validated against the generated worker Zod schema (156 apps, including 147 installed-only apps). `python3 tests/protocol.py ` checks persistent framing, bounded-request recovery, Unicode decoding, no-prompt status, invalid methods/permissions, permission gates and clean EOF. `tests/Fixture.swift` compiles into a disposable AppKit app with a text field, secure field, increment button/counter, slider, scroll area and a visual-only marker. `python3 tests/live.py ` requires both grants and verifies actual outcomes. `--allow-missing-screen` explicitly reports screenshots blocked rather than passing them; `--existing-fixture` leaves an existing fixture running for coordinated model trials. Give the fixture bundle ID `com.mothership.computer-use-fixture` when packaging it for integration tests. diff --git a/apps/desktop/native/computer-use/tests/Fixture.swift b/apps/desktop/native/computer-use/tests/Fixture.swift index bae9f2a4ceb..69aab0234e6 100644 --- a/apps/desktop/native/computer-use/tests/Fixture.swift +++ b/apps/desktop/native/computer-use/tests/Fixture.swift @@ -16,6 +16,13 @@ class ClickFocusField: NSTextField { final class FixedCaretField: ClickFocusField { override func setAccessibilitySelectedTextRange(_ accessibilitySelectedTextRange: NSRange) {} } +/// An element that reports its frame at infinity, as some apps (Maps) report hidden elements. +final class InfiniteFrameView: NSView { + override func isAccessibilityElement() -> Bool { true } + override func accessibilityRole() -> NSAccessibility.Role? { .group } + override func accessibilityLabel() -> String? { "Fixture infinite frame" } + override func accessibilityFrame() -> NSRect { NSRect(x: CGFloat.infinity, y: -CGFloat.infinity, width: 0, height: 0) } +} final class FixtureDelegate: NSObject, NSApplicationDelegate { var window: NSWindow! var count = 0 @@ -45,6 +52,7 @@ final class FixtureDelegate: NSObject, NSApplicationDelegate { let slider = NSSlider(value: 25, minValue: 0, maxValue: 100, target: nil, action: nil); slider.setAccessibilityLabel("Fixture slider") let time = NSDatePicker(); time.datePickerStyle = .textField; time.datePickerElements = .hourMinute; time.setAccessibilityLabel("Fixture time") time.dateValue = Calendar.current.date(bySettingHour: 10, minute: 15, second: 0, of: Date()) ?? Date(); time.target = self; time.action = #selector(timeChanged(_:)) + let infinite = InfiniteFrameView() let marker = MarkerView(); marker.setAccessibilityElement(false) marker.translatesAutoresizingMaskIntoConstraints = false let scroll = NSScrollView(); scroll.hasVerticalScroller = true @@ -54,7 +62,7 @@ final class FixtureDelegate: NSObject, NSApplicationDelegate { scroll.documentView = document scroll.contentView.postsBoundsChangedNotifications = true NotificationCenter.default.addObserver(self, selector: #selector(scrolled(_:)), name: NSView.boundsDidChangeNotification, object: scroll.contentView) - for view in [field, secure, button, counter, clickField, caretField, sheetButton, slider, time, timeLabel, marker, scrollCounter, scroll] { stack.addArrangedSubview(view) } + for view in [field, secure, button, counter, clickField, caretField, sheetButton, slider, time, timeLabel, marker, infinite, scrollCounter, scroll] { stack.addArrangedSubview(view) } window.contentView!.addSubview(stack) NSLayoutConstraint.activate([stack.leadingAnchor.constraint(equalTo: window.contentView!.leadingAnchor, constant: 30), stack.trailingAnchor.constraint(equalTo: window.contentView!.trailingAnchor, constant: -30), stack.topAnchor.constraint(equalTo: window.contentView!.topAnchor, constant: 30), field.widthAnchor.constraint(equalTo: stack.widthAnchor), secure.widthAnchor.constraint(equalTo: stack.widthAnchor), clickField.widthAnchor.constraint(equalTo: stack.widthAnchor), caretField.widthAnchor.constraint(equalTo: stack.widthAnchor), slider.widthAnchor.constraint(equalTo: stack.widthAnchor), scroll.widthAnchor.constraint(equalTo: stack.widthAnchor), scroll.heightAnchor.constraint(equalToConstant: 230), marker.widthAnchor.constraint(equalToConstant: 100), marker.heightAnchor.constraint(equalToConstant: 70)]) window.makeKeyAndOrderFront(nil) diff --git a/apps/desktop/native/computer-use/tests/NativeTests.swift b/apps/desktop/native/computer-use/tests/NativeTests.swift index c5a6d238c2c..dccb74ed7ef 100644 --- a/apps/desktop/native/computer-use/tests/NativeTests.swift +++ b/apps/desktop/native/computer-use/tests/NativeTests.swift @@ -26,6 +26,12 @@ import AppKit expectError("unsupported_key") { _ = try parseKey("F99") } expectError("invalid_arguments") { _ = try parseKey("Hyper+A") } expectError("invalid_arguments") { _ = try required(Optional.none, "example") } + var finitePoint = CGPoint(x: 10, y: 20), infinitePoint = CGPoint(x: CGFloat.infinity, y: -CGFloat.infinity) + var size = CGSize(width: 30, height: 40) + let sizeValue = AXValueCreate(.cgSize, &size) + precondition(axRect(position: AXValueCreate(.cgPoint, &finitePoint), size: sizeValue) == CGRect(x: 10, y: 20, width: 30, height: 40)) + precondition(axRect(position: AXValueCreate(.cgPoint, &infinitePoint), size: sizeValue) == nil) + expectError("reply_unencodable") { _ = try encodeReply(["id": "nonfinite", "result": ["x": Double.infinity]]) } for bundle in ["com.apple.systempreferences", "com.apple.Terminal", "COM.GOOGLECODE.ITERM2", "net.kovidgoyal.kitty", "com.1password.1password", "com.apple.Passwords", "com.bitwarden.desktop", "ai.sim.desktop", "com.simstudio.computer-use", "com.apple.ScriptEditor2", "com.apple.Automator", "com.apple.shortcuts"] { precondition(isProtectedApp(bundle), bundle) } diff --git a/apps/desktop/native/computer-use/tests/live.py b/apps/desktop/native/computer-use/tests/live.py index dbd97b93650..8a4ac1a2fb1 100644 --- a/apps/desktop/native/computer-use/tests/live.py +++ b/apps/desktop/native/computer-use/tests/live.py @@ -81,6 +81,8 @@ def coordinate(snapshot, node, fx=0.5, fy=0.5): time.sleep(1) snapshot = state(screenshot=True) assert 'fixture-only-secret' not in json.dumps(snapshot), 'Secure value leaked' + infinite = find(snapshot, label='Fixture infinite frame') + assert not {'x', 'y', 'width', 'height'} & infinite.keys(), f'An infinite frame reached the reply: {infinite}' image = snapshot.get('screenshot') if permissions['screenRecording']: assert image and base64.b64decode(image['base64']).startswith(b'\x89PNG\r\n\x1a\n'), snapshot.get('screenshotError') @@ -209,7 +211,7 @@ def batch(snapshot, steps): assert error['dispatchState'] == 'not_started', error assert find(state(), role='AXTextField', label='Fixture click-focus field').get('value') == 'replaced', 'A background app was clicked or typed into' call('activate_app', bundleId=bundle) - print('PASS: real fixture state, secure suppression, AX click/set/action, Unicode typing, keyboard chord, coordinate drag and scrolling, text refused by a slider, typing into a date picker, a window target typing into its focused editor, a title-bar click, a batch that applies, stops at a failing step, refuses before dispatch and stops when a sheet opens, click-to-focus for an editor that ignores Accessibility focus (inserting at the end, refusing an unconfirmed caret, and none in the background); every mutation verified in fresh state; screenshot=' + ('verified' if permissions['screenRecording'] else 'BLOCKED')) + print('PASS: real fixture state, secure suppression, an element reported at infinity read without a frame, AX click/set/action, Unicode typing, keyboard chord, coordinate drag and scrolling, text refused by a slider, typing into a date picker, a window target typing into its focused editor, a title-bar click, a batch that applies, stops at a failing step, refuses before dispatch and stops when a sheet opens, click-to-focus for an editor that ignores Accessibility focus (inserting at the end, refusing an unconfirmed caret, and none in the background); every mutation verified in fresh state; screenshot=' + ('verified' if permissions['screenRecording'] else 'BLOCKED')) finally: if fixture: fixture.terminate() diff --git a/apps/desktop/src/main/app-routes.ts b/apps/desktop/src/main/app-routes.ts index 22d0ad9f9e5..7f4065f2a17 100644 --- a/apps/desktop/src/main/app-routes.ts +++ b/apps/desktop/src/main/app-routes.ts @@ -65,3 +65,16 @@ export function settingsRoute(lastRoute: string | undefined): string { const organizationId = organizationIdFromRoute(lastRoute) return organizationId ? `/o/${organizationId}/settings/general` : SETTINGS_ENTRY_ROUTE } + +/** + * Route for the quick chat window: the web app's compact chat surface (the one the browser + * extension's side panel frames), scoped to the workspace or organization the user was last in. + */ +export function quickChatRoute(lastRoute: string | undefined): string { + const workspaceId = workspaceIdFromRoute(lastRoute) + if (workspaceId) { + return `/extension/${workspaceId}` + } + const organizationId = organizationIdFromRoute(lastRoute) + return organizationId ? `/extension/o/${organizationId}` : '/extension' +} diff --git a/apps/desktop/src/main/config.ts b/apps/desktop/src/main/config.ts index e9657914f57..983b58e963c 100644 --- a/apps/desktop/src/main/config.ts +++ b/apps/desktop/src/main/config.ts @@ -1,5 +1,9 @@ import { readFileSync } from 'node:fs' -import type { DesktopZoomPercent, TerminalAppearanceTheme } from '@sim/desktop-bridge' +import type { + DesktopZoomPercent, + QuickChatShortcut, + TerminalAppearanceTheme, +} from '@sim/desktop-bridge' import { createLogger } from '@sim/logger' import { isLoopbackHostname } from '@sim/security/ssrf' import { writeJsonFileAtomicallySync } from '@/main/atomic-json-file' @@ -109,6 +113,8 @@ export interface DesktopSettings { fullFileAccess?: boolean /** Keep the machine awake while a chat is running desktop work in the background. */ preventSleepWhileRunning?: boolean + /** Global shortcut for the quick chat window; null is off, absent is the platform default. */ + quickChatShortcut?: QuickChatShortcut | null /** Native app access is opt-in and cleared when the signed-in account changes. */ computerUseEnabled?: boolean /** Ask before operating an app for the first time; by default every unprotected app is allowed. */ diff --git a/apps/desktop/src/main/desktop-settings.test.ts b/apps/desktop/src/main/desktop-settings.test.ts index 436125a513c..cc10be1444f 100644 --- a/apps/desktop/src/main/desktop-settings.test.ts +++ b/apps/desktop/src/main/desktop-settings.test.ts @@ -30,6 +30,8 @@ function service(window: { url: string; focused: boolean } | null) { setBrowserDefaultZoom: vi.fn(), setTerminalDefaultZoom: vi.fn(), setPreventSleepWhileRunning: vi.fn(), + setQuickChatShortcut: vi.fn(), + isQuickChatShortcutAvailable: () => true, getDefaultBrowserDownloadDirectory: () => '/tmp', chooseBrowserDownloadDirectory: async () => null, }) diff --git a/apps/desktop/src/main/desktop-settings.ts b/apps/desktop/src/main/desktop-settings.ts index a237202a12f..26318e8b398 100644 --- a/apps/desktop/src/main/desktop-settings.ts +++ b/apps/desktop/src/main/desktop-settings.ts @@ -8,7 +8,9 @@ import { type DesktopZoomPercent, isDesktopAppearanceTheme, isDesktopZoomPercent, + isQuickChatShortcut, isTerminalAppearanceTheme, + type QuickChatShortcut, type TerminalThemeProfile, } from '@sim/desktop-bridge' import type { BrowserWindow } from 'electron' @@ -39,6 +41,7 @@ export interface DesktopSettingsService { setBrowserSearchSuggestionsEnabled(enabled: boolean): DesktopPreferences setFullFileAccess(enabled: boolean): DesktopPreferences setPreventSleepWhileRunning(enabled: boolean): DesktopPreferences + setQuickChatShortcut(shortcut: QuickChatShortcut | null): DesktopPreferences setAppearancePreference( key: DesktopAppearanceSettingKey, value: DesktopAppearanceTheme @@ -65,6 +68,9 @@ interface DesktopSettingsServiceDeps { setTerminalEnabled: (enabled: boolean) => void /** Starts or stops keeping the machine awake for background work already running. */ setPreventSleepWhileRunning: (enabled: boolean) => void + setQuickChatShortcut: (shortcut: QuickChatShortcut | null) => void + /** False while another app holds the quick chat shortcut. */ + isQuickChatShortcutAvailable: () => boolean /** Repaints current browser tabs when their persisted appearance changes. */ setBrowserTheme: (theme: DesktopAppearanceTheme) => void /** Applies a new default zoom to current and future browser tabs. */ @@ -79,6 +85,21 @@ interface DesktopSettingsServiceDeps { chooseBrowserDownloadDirectory: (defaultPath: string) => Promise } +/** + * ⌥Space is the macOS convention for an assistant launcher. On Windows and Linux every preset is + * already a common chord (the window menu, browser DevTools, editor hints), so the shortcut starts + * off there rather than silently taking one system-wide; the menu and tray still open quick chat. + */ +const DEFAULT_QUICK_CHAT_SHORTCUT: QuickChatShortcut | null = + process.platform === 'darwin' ? 'Alt+Space' : null + +/** The stored quick chat shortcut: null when turned off, the platform default when never chosen. */ +function readQuickChatShortcut(config: ConfigStore): QuickChatShortcut | null { + const stored = config.get('quickChatShortcut') + if (stored === null) return null + return isQuickChatShortcut(stored) ? stored : DEFAULT_QUICK_CHAT_SHORTCUT +} + function readPreferences( config: ConfigStore, defaultBrowserDownloadDirectory: string @@ -100,6 +121,7 @@ function readPreferences( terminalEnabled: config.get('terminalEnabled') ?? true, fullFileAccess: config.get('fullFileAccess') === true, preventSleepWhileRunning: config.get('preventSleepWhileRunning') ?? true, + quickChatShortcut: readQuickChatShortcut(config), browserTheme: isDesktopAppearanceTheme(browserTheme) ? browserTheme : 'app', browserDefaultZoom: isDesktopZoomPercent(browserDefaultZoom) ? browserDefaultZoom : 100, browserDownloadDirectory: @@ -135,7 +157,10 @@ function showsRoute(window: BrowserWindow, route: string | undefined): boolean { export function createDesktopSettingsService( deps: DesktopSettingsServiceDeps ): DesktopSettingsService { - const read = () => readPreferences(deps.config, deps.getDefaultBrowserDownloadDirectory()) + const read = (): DesktopPreferences => ({ + ...readPreferences(deps.config, deps.getDefaultBrowserDownloadDirectory()), + quickChatShortcutAvailable: deps.isQuickChatShortcutAvailable(), + }) const applyLaunchAtLogin = (enabled: boolean) => { // Registering an unpackaged Electron binary at login is surprising and @@ -201,6 +226,12 @@ export function createDesktopSettingsService( deps.setPreventSleepWhileRunning(enabled) return read() }, + setQuickChatShortcut(shortcut) { + deps.config.set('quickChatShortcut', shortcut) + deps.config.flush() + deps.setQuickChatShortcut(shortcut) + return read() + }, setAppearancePreference(key, value) { const previousBrowserTheme = key === 'browserTheme' ? read().browserTheme : undefined deps.config.set(key, value) diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index a0efc04faf5..c6781c8666d 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -28,7 +28,7 @@ import { retryAccountDataTeardown, waitForAccountDataMutations, } from '@/main/account-data-generation' -import { newChatRoute, settingsRoute } from '@/main/app-routes' +import { newChatRoute, quickChatRoute, settingsRoute } from '@/main/app-routes' import { activateBrowserScope as activateAgentBrowserScope, cancelTool as cancelAgentBrowserTool, @@ -67,7 +67,7 @@ import { isSafeInternalPath, partitionForOrigin, } from '@/main/config' -import { attachContextMenu } from '@/main/context-menu' +import { attachContextMenu, type ContextMenuDeps } from '@/main/context-menu' import { attachCspFallback } from '@/main/csp' import { DesktopChatSessionStore } from '@/main/desktop-chat-session-store' import { createApprovalNotifier } from '@/main/desktop-executor/approval-notifier' @@ -97,6 +97,7 @@ import { import { installApplicationMenu } from '@/main/menu' import { isAppOrigin, openExternalSafe } from '@/main/navigation' import { createEventLog, installMainProcessFailureObservers } from '@/main/observability' +import { createQuickChat } from '@/main/quick-chat' import { ScopedEventRouter } from '@/main/scoped-event-router' import { installGlobalGuards } from '@/main/security-guards' import { createServerWindow, relaunchApp } from '@/main/server-window' @@ -117,7 +118,7 @@ import { installTray, type TrayHandle } from '@/main/tray' import { checkForUpdatesInteractive, initUpdater, type UpdaterHandle } from '@/main/updater' import { installBrowserUserAgent } from '@/main/user-agent' import { createMainWindow, setupPermissionHandlers } from '@/main/window' -import { attachWindowOpenPolicy, isPopupContents } from '@/main/windows' +import { attachWindowOpenPolicy, isPopupContents, type WindowPolicyDeps } from '@/main/windows' const logger = createLogger('DesktopMain') @@ -433,6 +434,8 @@ function main(): void { }, }, { label: 'task resource state', clear: clearDesktopChatSessions }, + // The quick chat page belongs to the signed-out account and holds its chat. + { label: 'quick chat', clear: () => quickChat.close() }, { label: 'local filesystem grants', clear: clearLocalFileAccess }, { label: 'computer use', clear: () => computerUse.reset() }, ] @@ -497,6 +500,28 @@ function main(): void { } } + /** + * Window-open policy for app pages: internal URLs open as full Sim windows. `activate` brings + * the app forward for a caller that never activates it itself, like the macOS quick chat panel. + */ + function appWindowPolicy({ activate }: { activate: boolean }): WindowPolicyDeps { + return { + appOrigin, + openAppWindow: (url) => { + const route = routeFromAppUrl(url) + if (!route) return + const opened = createAndLoadAppWindow({ route }) + if (activate) void opened.then(showMainWindow) + }, + allowHttpLocalhost: allowHttpLocalhost(), + isCommittedRelaunchPending: () => committedRelaunchPending, + } + } + + function appContextMenuDeps(): ContextMenuDeps { + return { isDev: !app.isPackaged, allowHttpLocalhost: allowHttpLocalhost() } + } + async function createAndLoadAppWindow({ route: requestedRouteOverride, restorePosition = false, @@ -532,6 +557,8 @@ function main(): void { if (lastActiveWindow === win) { lastActiveWindow = getWindows().at(-1) ?? null } + // Closing the last full window quits on Windows and Linux; the quick chat must not keep the app alive. + if (process.platform !== 'darwin' && getWindows().length === 0) quickChat.close() }, }) windows.add(win) @@ -551,21 +578,8 @@ function main(): void { win.webContents.on('did-start-loading', () => { setBrowserAgentPanelBounds(null, win) }) - attachWindowOpenPolicy(win.webContents, { - appOrigin, - openAppWindow: (url) => { - const route = routeFromAppUrl(url) - if (route) { - void createAndLoadAppWindow({ route }) - } - }, - allowHttpLocalhost: allowHttpLocalhost(), - isCommittedRelaunchPending: () => committedRelaunchPending, - }) - attachContextMenu(win.webContents, { - isDev: !app.isPackaged, - allowHttpLocalhost: allowHttpLocalhost(), - }) + attachWindowOpenPolicy(win.webContents, appWindowPolicy({ activate: false })) + attachContextMenu(win.webContents, appContextMenuDeps()) const loadHealth = attachLoadHealth(win, { offlinePageUrl: (query) => localPageUrl('offline.html', query), getStartUrl: () => `${appOrigin()}${route}`, @@ -641,6 +655,20 @@ function main(): void { showMainWindow(win) } + const quickChat = createQuickChat({ + isAvailable: accountDataAvailable, + partition: () => { + // Configures the partition's permission and header handlers on first use, as a full window does. + ensureAppSession() + return appPartition() + }, + startUrl: () => `${appOrigin()}${quickChatRoute(config.get('lastRoute'))}`, + themeBackground: () => config.get('themeBackground'), + isPackaged: app.isPackaged, + windowPolicy: appWindowPolicy({ activate: true }), + contextMenu: appContextMenuDeps(), + }) + /** Installs or removes the menu-bar status item; safe to call repeatedly. */ function setTrayEnabled(enabled: boolean): void { if (!enabled) { @@ -654,6 +682,7 @@ function main(): void { appOrigin, lastRoute: () => config.get('lastRoute'), openMainWindow: (route) => void openMainWindowAt(route), + openQuickChat: quickChat.toggle, }) } } @@ -685,6 +714,8 @@ function main(): void { desktopExecutor.refreshRegistration() }, setPreventSleepWhileRunning: () => sleepBlocker.refresh(), + setQuickChatShortcut: quickChat.bindShortcut, + isQuickChatShortcutAvailable: quickChat.isShortcutAvailable, setBrowserTheme: setAgentBrowserTheme, setBrowserDefaultZoom: setAgentBrowserDefaultZoom, setTerminalDefaultZoom: (zoom) => { @@ -895,6 +926,7 @@ function main(): void { tray = null computerUse.cancel() if (computerStopShortcutRegistered) globalShortcut.unregister(computerStopShortcut) + quickChat.bindShortcut(null) localFilesystem.close() quiesceBrowserSessions() terminal.dispose() @@ -1148,6 +1180,7 @@ function main(): void { }, newWindow: () => void createAndLoadAppWindow(), newChat: () => void openMainWindowAt(newChatRoute(config.get('lastRoute'))), + openQuickChat: quickChat.toggle, handleFocusedResourceShortcut: (win, shortcut) => handleFocusedBrowserShortcut(shortcut, win) || terminal.handleFocusedShortcut(win, shortcut), @@ -1160,6 +1193,7 @@ function main(): void { }) installDocumentationHelpSearch() setTrayEnabled(config.get('trayEnabled') ?? true) + quickChat.bindShortcut(desktopSettings.getPreferences().quickChatShortcut ?? null) updater = initUpdater({ getWindow: getMainWindow, events, @@ -1172,9 +1206,11 @@ function main(): void { }, describeInFlightWork: async () => { // Native activity is read after the window probes so work started meanwhile still counts. - const windows = await countWindowsWithUnsavedChanges( - getWindows().map((win) => win.webContents) - ) + const quickChatPage = quickChat.webContents() + const windows = await countWindowsWithUnsavedChanges([ + ...getWindows().map((win) => win.webContents), + ...(quickChatPage ? [quickChatPage] : []), + ]) const terminalWork = await terminal.runningWork() const computerUseActivity = computerUse.getActivity() return describeInFlightWork({ diff --git a/apps/desktop/src/main/ipc.test.ts b/apps/desktop/src/main/ipc.test.ts index 030c9620362..c4fab9ad534 100644 --- a/apps/desktop/src/main/ipc.test.ts +++ b/apps/desktop/src/main/ipc.test.ts @@ -341,6 +341,7 @@ describe('registerIpcHandlers', () => { setBrowserSearchSuggestionsEnabled: vi.fn(), setFullFileAccess: vi.fn(), setPreventSleepWhileRunning: vi.fn(), + setQuickChatShortcut: vi.fn(), setAppearancePreference: vi.fn(), setBrowserDefaultZoom: vi.fn(), setTerminalDefaultZoom: vi.fn(), diff --git a/apps/desktop/src/main/ipc.ts b/apps/desktop/src/main/ipc.ts index 068b315b7e7..0d60c338715 100644 --- a/apps/desktop/src/main/ipc.ts +++ b/apps/desktop/src/main/ipc.ts @@ -23,6 +23,7 @@ import { isDesktopScopeId, isDesktopZoomPercent, isPendingDesktopScopeId, + isQuickChatShortcut, } from '@sim/desktop-bridge' import { createLogger } from '@sim/logger' import { @@ -942,6 +943,15 @@ export function registerIpcHandlers(deps: IpcDeps): void { ? deps.settings.setPreventSleepWhileRunning(enabled) : deps.settings.getPreferences(), }, + 'desktop:settings:set-quick-chat-shortcut': { + kind: 'invoke', + gate: 'app-origin', + denied: null, + handler: (shortcut) => + shortcut === null || isQuickChatShortcut(shortcut) + ? deps.settings.setQuickChatShortcut(shortcut) + : deps.settings.getPreferences(), + }, 'desktop:settings:set-appearance': { kind: 'invoke', gate: 'app-origin', diff --git a/apps/desktop/src/main/menu.test.ts b/apps/desktop/src/main/menu.test.ts index 2eb8862dc32..fc5eaed5b98 100644 --- a/apps/desktop/src/main/menu.test.ts +++ b/apps/desktop/src/main/menu.test.ts @@ -23,6 +23,7 @@ function makeDeps(origin = 'https://sim.ai'): MenuDeps { openFolderAccess: vi.fn(), newWindow: vi.fn(), newChat: vi.fn(), + openQuickChat: vi.fn(), handleFocusedResourceShortcut: vi.fn(() => false), toggleSidebar: vi.fn(), openSearch: vi.fn(), diff --git a/apps/desktop/src/main/menu.ts b/apps/desktop/src/main/menu.ts index b6bfb58fcf7..6e9e9e75597 100644 --- a/apps/desktop/src/main/menu.ts +++ b/apps/desktop/src/main/menu.ts @@ -22,6 +22,7 @@ export interface MenuDeps { openFolderAccess: (parent: BrowserWindow) => void newWindow: () => void newChat: () => void + openQuickChat: () => void /** * Menu accelerators are global, so the focused Browser or Terminal gets the * first chance to claim every resource shortcut before the Sim window uses @@ -227,6 +228,7 @@ export function buildMenuTemplate(deps: MenuDeps): MenuItemConstructorOptions[] click: deps.newWindow, }, { label: 'New Chat', accelerator: 'CmdOrCtrl+N', click: deps.newChat }, + { label: 'Quick Chat', click: deps.openQuickChat }, { type: 'separator' }, { label: 'Close Window', diff --git a/apps/desktop/src/main/quick-chat.ts b/apps/desktop/src/main/quick-chat.ts new file mode 100644 index 00000000000..a274e5e94f6 --- /dev/null +++ b/apps/desktop/src/main/quick-chat.ts @@ -0,0 +1,131 @@ +import type { QuickChatShortcut } from '@sim/desktop-bridge' +import { createLogger } from '@sim/logger' +import type { WebContents } from 'electron' +import { BrowserWindow, globalShortcut, nativeTheme, screen } from 'electron' +import { attachContextMenu, type ContextMenuDeps } from '@/main/context-menu' +import { classifyLoadError } from '@/main/load-health' +import { backgroundColorFor } from '@/main/shell-theme' +import { handleWillPreventUnload } from '@/main/window' +import { createSecureWebPreferences } from '@/main/window-preferences' +import { attachWindowOpenPolicy, type WindowPolicyDeps } from '@/main/windows' + +const logger = createLogger('QuickChat') + +const WIDTH = 440 +const HEIGHT = 620 +const MIN_WIDTH = 360 +const MIN_HEIGHT = 420 + +export interface QuickChatDeps { + /** False while account data is being torn down or cannot be stored; the window holds a signed-in page. */ + isAvailable: () => boolean + partition: () => string + /** The compact chat URL to open, scoped to where the user last was. */ + startUrl: () => string + themeBackground: () => 'dark' | 'light' | undefined + isPackaged: boolean + windowPolicy: WindowPolicyDeps + contextMenu: ContextMenuDeps +} + +export interface QuickChat { + /** Shows and focuses the window, creating it on first use, or hides it when it already has focus. */ + toggle(): void + /** Destroys the window and its page state; the next toggle starts a fresh chat. */ + close(): void + /** Replaces the global shortcut, or removes it with null. */ + bindShortcut(shortcut: QuickChatShortcut | null): void + /** False while another app holds the shortcut, so pressing it never reaches Sim. */ + isShortcutAvailable(): boolean + /** The open window's page, for checks that must see every app page before a relaunch. */ + webContents(): WebContents | null +} + +/** + * A small chat window that floats above every other app until the user closes it, toggled by a + * global shortcut. It loads the web app's compact chat surface with no preload: the page is a + * plain client of the app origin, so it reports no device capabilities and the agent never + * drives the browser, terminal, or apps from a window that sits on top of them. "Open in Sim" + * hands the chat to a full window, where those capabilities live. + */ +export function createQuickChat(deps: QuickChatDeps): QuickChat { + let win: BrowserWindow | null = null + let loadFailed = false + let shortcut: QuickChatShortcut | null = null + + const reveal = (target: BrowserWindow) => { + if (loadFailed) { + loadFailed = false + target.webContents.reload() + } + target.show() + target.focus() + } + + const open = (): BrowserWindow => { + loadFailed = false + const { workArea } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()) + const created = new BrowserWindow({ + title: 'Sim', + width: WIDTH, + height: HEIGHT, + minWidth: MIN_WIDTH, + minHeight: MIN_HEIGHT, + x: Math.round(workArea.x + (workArea.width - WIDTH) / 2), + y: Math.round(workArea.y + (workArea.height - HEIGHT) / 2), + // A macOS panel floats over full-screen apps and on every Space, and takes keyboard focus + // without activating Sim, so summoning it never pulls the main window forward. + ...(process.platform === 'darwin' ? { type: 'panel' } : { skipTaskbar: true }), + alwaysOnTop: true, + fullscreenable: false, + minimizable: false, + maximizable: false, + show: false, + backgroundColor: backgroundColorFor(deps.themeBackground(), nativeTheme.shouldUseDarkColors), + webPreferences: createSecureWebPreferences(deps.partition(), null, deps.isPackaged), + }) + created.once('ready-to-show', () => reveal(created)) + created.on('closed', () => { + if (win === created) win = null + }) + created.webContents.on('did-fail-load', (_event, errorCode, _description, url, isMainFrame) => { + if (isMainFrame && classifyLoadError(errorCode, url) !== 'ignored') loadFailed = true + }) + // A crashed page cannot recover in place; the next toggle builds a fresh window instead. + created.webContents.on('render-process-gone', () => created.destroy()) + created.webContents.on('will-prevent-unload', (event) => { + handleWillPreventUnload(created, event, deps.windowPolicy.isCommittedRelaunchPending()) + }) + attachWindowOpenPolicy(created.webContents, deps.windowPolicy) + attachContextMenu(created.webContents, deps.contextMenu) + void created.loadURL(deps.startUrl()).catch(() => {}) + return created + } + + const toggle = () => { + if (!win || win.isDestroyed()) { + if (deps.isAvailable()) win = open() + return + } + if (win.isVisible() && win.isFocused()) win.hide() + else reveal(win) + } + + return { + toggle, + close() { + if (win && !win.isDestroyed()) win.destroy() + win = null + }, + bindShortcut(next) { + if (next === shortcut && (!next || globalShortcut.isRegistered(next))) return + if (shortcut) globalShortcut.unregister(shortcut) + shortcut = next + if (next && !globalShortcut.register(next, toggle)) { + logger.warn('Quick chat shortcut is held by another app', { shortcut: next }) + } + }, + isShortcutAvailable: () => !shortcut || globalShortcut.isRegistered(shortcut), + webContents: () => (win && !win.isDestroyed() ? win.webContents : null), + } +} diff --git a/apps/desktop/src/main/tray.test.ts b/apps/desktop/src/main/tray.test.ts index d17830b2df7..1d9c3bb4fb8 100644 --- a/apps/desktop/src/main/tray.test.ts +++ b/apps/desktop/src/main/tray.test.ts @@ -13,6 +13,7 @@ function makeDeps(overrides: Partial = {}): TrayDeps { appOrigin: () => 'https://sim.ai', lastRoute: () => '/workspace/ws1/home', openMainWindow: vi.fn(), + openQuickChat: vi.fn(), ...overrides, } } diff --git a/apps/desktop/src/main/tray.ts b/apps/desktop/src/main/tray.ts index 7ae06b73c72..0bf3ea58936 100644 --- a/apps/desktop/src/main/tray.ts +++ b/apps/desktop/src/main/tray.ts @@ -332,6 +332,7 @@ export interface TrayDeps { appOrigin: () => string lastRoute: () => string | undefined openMainWindow: (route?: string) => void + openQuickChat: () => void } function chatMenuItem(chat: RecentChat, deps: TrayDeps): MenuItemConstructorOptions { @@ -373,6 +374,7 @@ export function buildTrayMenuTemplate( } template.push( { label: 'New Chat', click: () => deps.openMainWindow(newChatRoute(deps.lastRoute())) }, + { label: 'Quick Chat', click: deps.openQuickChat }, { label: 'Open Sim', click: () => deps.openMainWindow() }, { type: 'separator' }, // Plain item, not role:'quit' — macOS Tahoe auto-decorates standard roles diff --git a/apps/desktop/src/main/window-preferences.ts b/apps/desktop/src/main/window-preferences.ts index 9a92f224a58..77382bfbf57 100644 --- a/apps/desktop/src/main/window-preferences.ts +++ b/apps/desktop/src/main/window-preferences.ts @@ -6,10 +6,13 @@ import { app } from 'electron' * The preload injects nothing into the page; it only exposes a whitelisted * IPC bridge. The shell version rides in as a preload argv flag so the web * app can enforce its minimum shell version without an IPC round-trip. + * + * A null `preloadPath` gives a plain web page with no bridge at all, for + * surfaces that must not report device capabilities (see `quick-chat.ts`). */ export function createSecureWebPreferences( partition: string, - preloadPath: string, + preloadPath: string | null, isPackaged: boolean ): WebPreferences { return { @@ -21,7 +24,7 @@ export function createSecureWebPreferences( devTools: !isPackaged, spellcheck: true, partition, - preload: preloadPath, + preload: preloadPath ?? undefined, additionalArguments: [`--sim-desktop-version=${app.getVersion()}`], } } diff --git a/apps/desktop/src/preload/index.ts b/apps/desktop/src/preload/index.ts index 7f06f2b5180..e4a0c216116 100644 --- a/apps/desktop/src/preload/index.ts +++ b/apps/desktop/src/preload/index.ts @@ -46,6 +46,7 @@ import type { DesktopZoomPercent, LocalFilesystemRequest, LocalFilesystemResponse, + QuickChatShortcut, SimDesktopApi, TerminalShortcutCommand, TerminalThemeProfile, @@ -217,6 +218,8 @@ const api: SimDesktopApi = { }, setPreventSleepWhileRunning: (enabled: boolean): Promise => ipcRenderer.invoke('desktop:settings:set-prevent-sleep', enabled), + setQuickChatShortcut: (shortcut: QuickChatShortcut | null): Promise => + ipcRenderer.invoke('desktop:settings:set-quick-chat-shortcut', shortcut), setBrowserSearchSuggestionsEnabled: (enabled: boolean): Promise => ipcRenderer.invoke('desktop:settings:set-browser-search-suggestions', enabled), notify: (payload: DesktopNotificationPayload): Promise => diff --git a/apps/docs/content/docs/integrations/airtable.mdx b/apps/docs/content/docs/integrations/airtable.mdx index 6d817aba22d..6045425c5b6 100644 --- a/apps/docs/content/docs/integrations/airtable.mdx +++ b/apps/docs/content/docs/integrations/airtable.mdx @@ -59,7 +59,7 @@ List all bases the authenticated user has access to | `bases` | array | Array of Airtable bases with id, name, and permissionLevel | | ↳ `id` | string | Base ID \(starts with "app"\) | | ↳ `name` | string | Base name | -| ↳ `permissionLevel` | string | Permission level \(none, read, comment, edit, create\) | +| ↳ `permissionLevel` | string | Permission level \(none, read, comment, edit, create, interfaceOnly, permissionSetOnly\) | | `metadata` | json | Pagination and count metadata | | ↳ `offset` | string | Offset for next page of results | | ↳ `totalBases` | number | Number of bases returned | @@ -111,8 +111,11 @@ Read records from an Airtable table | --------- | ---- | -------- | ----------- | | `baseId` | string | Yes | Airtable base ID \(starts with "app", e.g., "appXXXXXXXXXXXXXX"\) | | `tableId` | string | Yes | Table ID \(starts with "tbl"\) or table name | -| `maxRecords` | number | No | Maximum number of records to return \(default: all records\) | +| `maxRecords` | number | No | Maximum total number of records to return. Each request returns at most one page \(pageSize, up to 100\); use offset to fetch the next page | | `filterFormula` | string | No | Formula to filter records \(e.g., "\(\{Field Name\} = 'Value'\)"\) | +| `pageSize` | number | No | Number of records per page \(max 100, default 100\) | +| `offset` | string | No | Pagination offset from a previous response, to fetch the next page of records | +| `view` | string | No | View name or ID. Only records in the view are returned, sorted in the view order | #### Output diff --git a/apps/docs/content/docs/integrations/amplitude.mdx b/apps/docs/content/docs/integrations/amplitude.mdx index 51cffa5efad..f6d9183186d 100644 --- a/apps/docs/content/docs/integrations/amplitude.mdx +++ b/apps/docs/content/docs/integrations/amplitude.mdx @@ -194,7 +194,7 @@ Get a user profile including properties, cohort memberships, and computed proper | `deviceId` | string | Device ID | | `ampProps` | json | Amplitude user properties \(library, first_used, last_used, custom properties\) | | `cohortIds` | array | List of cohort IDs the user belongs to | -| `computations` | json | Computed user properties | +| `computations` | json | Computed user properties when Amplitude returns them under a computations key; computed values are otherwise returned within ampProps | @@ -209,13 +209,14 @@ Query event analytics data with segmentation. Get event counts, uniques, average | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `eventType` | string | Yes | Event type name to analyze | +| `secondEventType` | string | No | Second event type to query alongside the first \(event B in a formula such as "UNIQUES\(A\)/UNIQUES\(B\)"\) | | `start` | string | Yes | Start date in YYYYMMDD format | | `end` | string | Yes | End date in YYYYMMDD format | | `metric` | string | No | Metric type: uniques, totals, pct_dau, average, histogram, sums, value_avg, or formula \(default: uniques\) | -| `interval` | string | No | Time interval: 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) | +| `interval` | string | No | Time interval: -300000 \(real-time\), -3600000 \(hourly\), 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) | | `groupBy` | string | No | Property name to group by \(prefix custom user properties with "gp:"\) | | `groupBy2` | string | No | Second property name to group by \(prefix custom user properties with "gp:"\) | -| `limit` | string | No | Maximum number of group-by values \(max 1000\) | +| `limit` | string | No | Maximum number of group-by values \(default 1000, max 20000\) | | `filters` | string | No | JSON array of filter objects applied to the event, e.g. \[\{"subprop_type":"event","subprop_key":"city","subprop_op":"is","subprop_value":\["San Francisco"\]\}\] | | `formula` | string | No | Required when metric is "formula", e.g. "UNIQUES\(A\)/UNIQUES\(B\)" | | `segment` | string | No | JSON segment definition\(s\) applied to the query | @@ -351,7 +352,7 @@ Analyze conversion rates and drop-off between a sequence of events. | `interval` | string | No | Time interval: -300000 \(real-time\), -3600000 \(hourly\), 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) | | `conversionWindowSeconds` | string | No | Conversion window in seconds \(default: 2592000, i.e. 30 days\) | | `groupBy` | string | No | Property to group by \(limit: one; prefix custom properties with "gp:"\) | -| `limit` | string | No | Maximum number of group-by values \(default: 100, max: 1000\) | +| `limit` | string | No | Maximum number of group-by values \(default and max: 1000\) | | `segment` | string | No | JSON segment definition\(s\) applied to the query | #### Output @@ -359,9 +360,9 @@ Analyze conversion rates and drop-off between a sequence of events. | Parameter | Type | Description | | --------- | ---- | ----------- | | `funnels` | array | Funnel results, one entry per segment | -| ↳ `stepByStep` | json | Conversion count at each step | -| ↳ `cumulative` | json | Cumulative conversion percentage at each step | -| ↳ `cumulativeRaw` | json | Cumulative conversion count at each step | +| ↳ `stepByStep` | json | Fraction of users from the previous step who completed each step | +| ↳ `cumulative` | json | Fraction of total users who completed each step | +| ↳ `cumulativeRaw` | json | Number of users who completed each step | | ↳ `medianTransTimes` | json | Median transition time between steps \(ms\) | | ↳ `avgTransTimes` | json | Average transition time between steps \(ms\) | | ↳ `events` | json | Event names for each funnel step | @@ -383,8 +384,8 @@ Measure how many users return to perform an action after a starting action. | `returnEvent` | string | Yes | JSON returning event object, e.g. \{"event_type":"_all"\} or \{"event_type":"_active"\} | | `start` | string | Yes | Start date in YYYYMMDD format | | `end` | string | Yes | End date in YYYYMMDD format | -| `retentionMode` | string | No | Retention type: "bracket", "rolling", or "n-day" \(default: n-day\) | -| `retentionBrackets` | string | No | Required when Retention Mode is "bracket". Day ranges, e.g. \[\[0,4\]\] | +| `retentionMode` | string | No | Retention type: "n-day" \(sent as nday\), "rolling", "bracket", or "nday_or_before" \(default: n-day\) | +| `retentionBrackets` | string | No | Required when Retention Mode is "bracket". Day ranges with an exclusive end day, e.g. \[\[0,5\]\] for Day 0 to Day 4 | | `interval` | string | No | Time interval: 1 \(daily\), 7 \(weekly\), or 30 \(monthly\) | | `groupBy` | string | No | Property to group by \(limit: one; prefix custom properties with "gp:"\) | | `segment` | string | No | JSON segment definition\(s\) applied to the query | diff --git a/apps/docs/content/docs/integrations/asana.mdx b/apps/docs/content/docs/integrations/asana.mdx index d5bec6b792d..668a6e74f94 100644 --- a/apps/docs/content/docs/integrations/asana.mdx +++ b/apps/docs/content/docs/integrations/asana.mdx @@ -45,9 +45,11 @@ Retrieve a single task by GID or get multiple tasks with filters | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `taskGid` | string | No | The globally unique identifier \(GID\) of the task. If not provided, will get multiple tasks. | -| `workspace` | string | No | Asana workspace GID \(numeric string\) to filter tasks \(required when not using taskGid\) | +| `workspace` | string | No | Asana workspace GID \(numeric string\) to filter tasks. Asana requires an assignee alongside a workspace filter; use project instead to list a project | +| `assignee` | string | No | Assignee to filter tasks on \(user GID, email, or "me"\). Required by Asana when filtering by workspace; ignored when filtering by project | | `project` | string | No | Asana project GID \(numeric string\) to filter tasks | -| `limit` | number | No | Maximum number of tasks to return \(default: 50\) | +| `limit` | number | No | Maximum number of tasks to return, 1-100 \(default: 50\) | +| `offset` | string | No | Pagination offset token from a previous response next_page.offset | #### Output @@ -74,6 +76,10 @@ Retrieve a single task by GID or get multiple tasks with filters | ↳ `gid` | string | Task GID | | ↳ `name` | string | Task name | | ↳ `completed` | boolean | Completion status | +| `next_page` | object | Pagination info when fetching multiple tasks; null on the last page | +| ↳ `offset` | string | Offset token for the next page | +| ↳ `path` | string | API path | +| ↳ `uri` | string | Full URI | @@ -175,10 +181,11 @@ Search for tasks in an Asana workspace | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `workspace` | string | Yes | Asana workspace GID \(numeric string\) to search tasks in | -| `text` | string | No | Text to search for in task names | +| `text` | string | No | Full-text search on task names and descriptions | | `assignee` | string | No | Filter tasks by assignee user GID | | `projects` | array | No | Array of Asana project GIDs \(numeric strings\) to filter tasks by | | `completed` | boolean | No | Filter by completion status | +| `limit` | number | No | Maximum number of tasks to return, 1-100 | #### Output @@ -199,7 +206,7 @@ Search for tasks in an Asana workspace | ↳ `due_on` | string | Due date | | ↳ `created_at` | string | Creation timestamp | | ↳ `modified_at` | string | Modified timestamp | -| `next_page` | object | Pagination info | +| `next_page` | object | Not returned by Asana search, which has no pagination; sort by created_at and filter to page manually | | ↳ `offset` | string | Offset token | | ↳ `path` | string | API path | | ↳ `uri` | string | Full URI | diff --git a/apps/docs/content/docs/integrations/box.mdx b/apps/docs/content/docs/integrations/box.mdx index 60527a81323..9257502b031 100644 --- a/apps/docs/content/docs/integrations/box.mdx +++ b/apps/docs/content/docs/integrations/box.mdx @@ -139,7 +139,9 @@ List files and folders in a Box folder | --------- | ---- | -------- | ----------- | | `folderId` | string | Yes | The ID of the folder to list items from \(use "0" for root\) | | `limit` | number | No | Maximum number of items to return per page | -| `offset` | number | No | The offset for pagination | +| `offset` | number | No | The offset for pagination \(ignored when using marker-based pagination\) | +| `usemarker` | boolean | No | Use marker-based pagination instead of offsets \(recommended for large folders\); returns nextMarker | +| `marker` | string | No | Marker from a previous response to fetch the next page \(implies usemarker\) | | `sort` | string | No | Sort field: id, name, date, or size | | `direction` | string | No | Sort direction: ASC or DESC | @@ -154,9 +156,10 @@ List files and folders in a Box folder | ↳ `size` | number | Item size in bytes | | ↳ `createdAt` | string | Creation timestamp | | ↳ `modifiedAt` | string | Last modified timestamp | -| `totalCount` | number | Total number of items in the folder | +| `totalCount` | number | Total number of items in the folder; null with marker pagination, where Box omits it | | `offset` | number | Current pagination offset | | `limit` | number | Current pagination limit | +| `nextMarker` | string | Marker for the next page when using marker-based pagination | @@ -371,7 +374,7 @@ Create a new Box Sign request to send documents for e-signature | Parameter | Type | Description | | --------- | ---- | ----------- | | `id` | string | Sign request ID | -| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) | +| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, error, error_converting, error_sending, expired, finalizing, error_finalizing\) | | `name` | string | Sign request name | | `shortId` | string | Human-readable short ID | | `signers` | array | List of signers | @@ -384,7 +387,7 @@ Create a new Box Sign request to send documents for e-signature | `sourceFiles` | array | Source files for signing | | ↳ `id` | string | File ID | | ↳ `type` | string | File type | -| ↳ `name` | string | File name | +| ↳ `name` | string | File name \(Box Sign returns source files without a name, so this is usually empty\) | | `emailSubject` | string | Custom email subject line | | `emailMessage` | string | Custom email message body | | `daysValid` | number | Number of days the request is valid | @@ -412,7 +415,7 @@ Get the details and status of a Box Sign request | Parameter | Type | Description | | --------- | ---- | ----------- | | `id` | string | Sign request ID | -| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) | +| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, error, error_converting, error_sending, expired, finalizing, error_finalizing\) | | `name` | string | Sign request name | | `shortId` | string | Human-readable short ID | | `signers` | array | List of signers | @@ -425,7 +428,7 @@ Get the details and status of a Box Sign request | `sourceFiles` | array | Source files for signing | | ↳ `id` | string | File ID | | ↳ `type` | string | File type | -| ↳ `name` | string | File name | +| ↳ `name` | string | File name \(Box Sign returns source files without a name, so this is usually empty\) | | `emailSubject` | string | Custom email subject line | | `emailMessage` | string | Custom email message body | | `daysValid` | number | Number of days the request is valid | @@ -455,7 +458,7 @@ List all Box Sign requests | --------- | ---- | ----------- | | `signRequests` | array | List of sign requests | | ↳ `id` | string | Sign request ID | -| ↳ `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) | +| ↳ `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, error, error_converting, error_sending, expired, finalizing, error_finalizing\) | | ↳ `name` | string | Sign request name | | ↳ `shortId` | string | Human-readable short ID | | ↳ `signers` | array | List of signers | @@ -468,7 +471,7 @@ List all Box Sign requests | ↳ `sourceFiles` | array | Source files for signing | | ↳ `id` | string | File ID | | ↳ `type` | string | File type | -| ↳ `name` | string | File name | +| ↳ `name` | string | File name \(Box Sign returns source files without a name, so this is usually empty\) | | ↳ `emailSubject` | string | Custom email subject line | | ↳ `emailMessage` | string | Custom email message body | | ↳ `daysValid` | number | Number of days the request is valid | @@ -498,7 +501,7 @@ Cancel a pending Box Sign request | Parameter | Type | Description | | --------- | ---- | ----------- | | `id` | string | Sign request ID | -| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing\) | +| `status` | string | Request status \(converting, created, sent, viewed, signed, cancelled, declined, error, error_converting, error_sending, expired, finalizing, error_finalizing\) | | `name` | string | Sign request name | | `shortId` | string | Human-readable short ID | | `signers` | array | List of signers | @@ -511,7 +514,7 @@ Cancel a pending Box Sign request | `sourceFiles` | array | Source files for signing | | ↳ `id` | string | File ID | | ↳ `type` | string | File type | -| ↳ `name` | string | File name | +| ↳ `name` | string | File name \(Box Sign returns source files without a name, so this is usually empty\) | | `emailSubject` | string | Custom email subject line | | `emailMessage` | string | Custom email message body | | `daysValid` | number | Number of days the request is valid | diff --git a/apps/docs/content/docs/integrations/clickup.mdx b/apps/docs/content/docs/integrations/clickup.mdx index 451e3417e47..57169e8c67d 100644 --- a/apps/docs/content/docs/integrations/clickup.mdx +++ b/apps/docs/content/docs/integrations/clickup.mdx @@ -147,7 +147,7 @@ Delete a task from ClickUp ### ClickUp Get Tasks -List the tasks in a ClickUp list (100 tasks per page; increment page until an empty result to paginate) +List the tasks in a ClickUp list (100 tasks per page; increment page until lastPage is true to paginate) @@ -168,12 +168,17 @@ List the tasks in a ClickUp list (100 tasks per page; increment page until an em | `tags` | array | No | Filter tasks by tag names | | `dueDateGt` | number | No | Only tasks due after this Unix timestamp in milliseconds | | `dueDateLt` | number | No | Only tasks due before this Unix timestamp in milliseconds | +| `dateCreatedGt` | number | No | Only tasks created after this Unix timestamp in milliseconds | +| `dateCreatedLt` | number | No | Only tasks created before this Unix timestamp in milliseconds | +| `dateUpdatedGt` | number | No | Only tasks updated after this Unix timestamp in milliseconds | +| `dateUpdatedLt` | number | No | Only tasks updated before this Unix timestamp in milliseconds | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | | `tasks` | array | Tasks in the list | +| `lastPage` | boolean | Whether this is the last page of results | @@ -202,6 +207,10 @@ Search tasks across a ClickUp workspace with filters for lists, folders, spaces, | `tags` | array | No | Filter tasks by tag names | | `dueDateGt` | number | No | Only tasks due after this Unix timestamp in milliseconds | | `dueDateLt` | number | No | Only tasks due before this Unix timestamp in milliseconds | +| `dateCreatedGt` | number | No | Only tasks created after this Unix timestamp in milliseconds | +| `dateCreatedLt` | number | No | Only tasks created before this Unix timestamp in milliseconds | +| `dateUpdatedGt` | number | No | Only tasks updated after this Unix timestamp in milliseconds | +| `dateUpdatedLt` | number | No | Only tasks updated before this Unix timestamp in milliseconds | #### Output diff --git a/apps/docs/content/docs/integrations/docusign.mdx b/apps/docs/content/docs/integrations/docusign.mdx index b17fd5ce500..4495f739d6a 100644 --- a/apps/docs/content/docs/integrations/docusign.mdx +++ b/apps/docs/content/docs/integrations/docusign.mdx @@ -53,7 +53,7 @@ Create and send a DocuSign envelope with a document for e-signature | `signerName` | string | Yes | Full name of the signer | | `ccEmail` | string | No | Email address of carbon copy recipient | | `ccName` | string | No | Full name of carbon copy recipient | -| `file` | file | No | Document file to send for signature | +| `file` | file | No | Document file to send for signature. Text "/sig1/" and "/date1/" in the document anchor the signature and date fields; without them the signer places fields freely, unless the DocuSign account disables freeform signing | | `status` | string | No | Envelope status: "sent" to send immediately, "created" for draft \(default: "sent"\) | #### Output @@ -137,7 +137,8 @@ List envelopes from your DocuSign account with optional filters | `toDate` | string | No | End date filter \(ISO 8601\) | | `envelopeStatus` | string | No | Filter by status: created, sent, delivered, completed, declined, voided | | `searchText` | string | No | Search text to filter envelopes | -| `count` | string | No | Maximum number of envelopes to return \(default: 25\) | +| `count` | string | No | Maximum number of envelopes to return \(max 1000\) | +| `startPosition` | string | No | Zero-based index of the first result to return, for paging through envelopes \(default: 0\) | #### Output @@ -153,6 +154,9 @@ List envelopes from your DocuSign account with optional filters | ↳ `statusChangedDateTime` | string | ISO 8601 datetime of last status change | | `totalSetSize` | number | Total number of matching envelopes | | `resultSetSize` | number | Number of envelopes returned in this response | +| `startPosition` | number | Zero-based index of the first result in this page | +| `endPosition` | number | Zero-based index of the last result in this page | +| `nextUri` | string | URI of the next page of results; null on the last page | @@ -213,7 +217,8 @@ List available templates in your DocuSign account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `searchText` | string | No | Search text to filter templates by name | -| `count` | string | No | Maximum number of templates to return | +| `count` | string | No | Maximum number of templates to return \(DocuSign returns up to 2000 when omitted\) | +| `startPosition` | string | No | Zero-based index of the first result to return, for paging through templates \(default: 0\) | #### Output @@ -228,6 +233,9 @@ List available templates in your DocuSign account | ↳ `lastModified` | string | ISO 8601 last modified date | | `totalSetSize` | number | Total number of matching templates | | `resultSetSize` | number | Number of templates returned in this response | +| `startPosition` | number | Zero-based index of the first result in this page | +| `endPosition` | number | Zero-based index of the last result in this page | +| `nextUri` | string | URI of the next page of results; null on the last page | diff --git a/apps/docs/content/docs/integrations/dropbox.mdx b/apps/docs/content/docs/integrations/dropbox.mdx index bd8d0ed4850..820a9e396fc 100644 --- a/apps/docs/content/docs/integrations/dropbox.mdx +++ b/apps/docs/content/docs/integrations/dropbox.mdx @@ -104,11 +104,12 @@ List the contents of a folder in Dropbox | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `path` | string | Yes | The path of the folder to list \(use "" for root\) | +| `path` | string | No | The path of the folder to list \(use "" for root\). Required unless cursor is set | | `recursive` | boolean | No | If true, list contents recursively | | `includeDeleted` | boolean | No | If true, include deleted files/folders | | `includeMediaInfo` | boolean | No | If true, include media info for photos/videos | -| `limit` | number | No | Maximum number of results to return \(default: 500\) | +| `limit` | number | No | Approximate maximum number of entries to return per page \(1-2000\) | +| `cursor` | string | No | Cursor from a previous List Folder call to fetch the next page via list_folder/continue \(other options are ignored when set\) | #### Output @@ -119,7 +120,7 @@ List the contents of a folder in Dropbox | ↳ `name` | string | Name of the file/folder | | ↳ `path_display` | string | Display path | | ↳ `size` | number | Size in bytes \(files only\) | -| `cursor` | string | Cursor for pagination | +| `cursor` | string | Cursor to pass back as the cursor input to fetch the next page | | `hasMore` | boolean | Whether there are more results | @@ -321,10 +322,11 @@ Search for files and folders in Dropbox | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `query` | string | Yes | The search query | +| `query` | string | No | The search query. Required unless cursor is set | | `path` | string | No | Dropbox folder path to limit search scope \(e.g., /folder/subfolder\) | | `fileExtensions` | string | No | Comma-separated list of file extensions to filter by \(e.g., pdf,xlsx\) | | `maxResults` | number | No | Maximum number of results to return \(default: 100\) | +| `cursor` | string | No | Cursor from a previous Search call to fetch the next page via search/continue_v2 \(other options are ignored when set\) | #### Output @@ -334,7 +336,7 @@ Search for files and folders in Dropbox | ↳ `match_type` | object | Type of match: filename, content, or both | | ↳ `metadata` | object | File or folder metadata | | `hasMore` | boolean | Whether there are more results | -| `cursor` | string | Cursor for pagination | +| `cursor` | string | Cursor to pass back as the cursor input to fetch the next page | diff --git a/apps/docs/content/docs/integrations/intercom.mdx b/apps/docs/content/docs/integrations/intercom.mdx index 44c2884ee57..afe8d2314a2 100644 --- a/apps/docs/content/docs/integrations/intercom.mdx +++ b/apps/docs/content/docs/integrations/intercom.mdx @@ -784,7 +784,7 @@ Create and send a new admin-initiated message in Intercom. Returns API-aligned f | `body` | string | Yes | The body of the message | | `from_type` | string | Yes | Sender type: "admin" | | `from_id` | string | Yes | The ID of the admin sending the message | -| `to_type` | string | Yes | Recipient type: "contact" | +| `to_type` | string | Yes | Recipient role: "user" or "lead" | | `to_id` | string | Yes | The ID of the contact receiving the message | | `created_at` | number | No | Unix timestamp for when the message was created. If not provided, current time is used. | diff --git a/apps/docs/content/docs/integrations/mailchimp.mdx b/apps/docs/content/docs/integrations/mailchimp.mdx index f77a710ed18..486dc1c1d55 100644 --- a/apps/docs/content/docs/integrations/mailchimp.mdx +++ b/apps/docs/content/docs/integrations/mailchimp.mdx @@ -14,9 +14,9 @@ Use [Mailchimp](https://mailchimp.com/) in Sim to manage audiences and members, ## Member removal -**Delete Member from Mailchimp Audience** calls Mailchimp's reversible archive endpoint. **Archive Member from Mailchimp Audience** instead targets permanent deletion of identifying data, after which the member cannot be re-imported. These UI labels differ from the provider's terminology. +**Delete Member from Mailchimp Audience** and **Archive Member from Mailchimp Audience** both call Mailchimp's reversible [archive endpoint](https://mailchimp.com/developer/marketing/api/list-members/archive-list-member); an archived member can be added back. -The current permanent-deletion action sends `DELETE`, while [Mailchimp documents `POST` for delete-permanent](https://mailchimp.com/developer/marketing/api/lists/members/create-member-action-delete-permanent). Its compatibility is unverified; do not rely on it for deletion until the request method is corrected and tested. +**Permanently Delete Member from Mailchimp Audience** calls [`POST .../actions/delete-permanent`](https://mailchimp.com/developer/marketing/api/lists/members/create-member-action-delete-permanent), which erases the member's identifying data. It cannot be undone, and the address cannot be re-imported. {/* MANUAL-CONTENT-END */} ## Use the block [#usage-instructions] @@ -87,7 +87,7 @@ Create a new audience (list) in Mailchimp | `contact` | string | Yes | JSON object of contact information \(e.g., \{"company": "Acme", "address1": "123 Main St", "city": "NYC", "state": "NY", "zip": "10001", "country": "US"\}\) | | `permissionReminder` | string | Yes | Permission reminder text shown to subscribers \(e.g., "You signed up for updates on our website"\) | | `campaignDefaults` | string | Yes | JSON object of default campaign settings \(e.g., \{"from_name": "Acme", "from_email": "news@acme.com", "subject": "", "language": "en"\}\) | -| `emailTypeOption` | string | Yes | Support multiple email formats: "true" or "false" | +| `emailTypeOption` | string | No | Support multiple email formats: "true" or "false" \(default "true"\) | #### Output @@ -115,7 +115,7 @@ Update an existing audience (list) in Mailchimp | `audienceName` | string | No | The name of the audience/list \(e.g., "Newsletter Subscribers"\) | | `permissionReminder` | string | No | Permission reminder text shown to subscribers \(e.g., "You signed up for updates on our website"\) | | `campaignDefaults` | string | No | JSON object of default campaign settings \(e.g., \{"from_name": "Acme", "from_email": "news@acme.com"\}\) | -| `emailTypeOption` | string | No | Support multiple email formats: "true" or "false" | +| `emailTypeOption` | string | No | Whether the audience supports multiple email formats \("true" or "false"\). Leave unset to keep the current setting | #### Output @@ -307,9 +307,32 @@ Archive a member from a Mailchimp audience without permanently deleting their da +### Permanently Delete Member from Mailchimp Audience + +Permanently delete a member and their personally identifiable data from a Mailchimp audience. This cannot be undone, and the address cannot be re-imported. + + + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `listId` | string | Yes | The unique ID for the audience/list \(e.g., "abc123def4"\) | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash of the lowercase email | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Permanent deletion confirmation | +| ↳ `success` | boolean | Operation success | + + + ### Archive Member from Mailchimp Audience -Request permanent deletion of a member and their identifying data. This targets delete-permanent, despite the Archive Member label; provider acceptance of the current HTTP method is unverified. +Archive a member in a Mailchimp audience. Archived contacts keep their data and can be restored with Unarchive Member. @@ -325,7 +348,7 @@ Request permanent deletion of a member and their identifying data. This targets | Parameter | Type | Description | | --------- | ---- | ----------- | | `success` | boolean | Operation success status | -| `output` | object | Permanent-deletion request confirmation | +| `output` | object | Archive confirmation | | ↳ `success` | boolean | Operation success | @@ -1137,7 +1160,7 @@ Add tags to a member in a Mailchimp audience | --------- | ---- | -------- | ----------- | | `listId` | string | Yes | The unique ID for the audience/list \(e.g., "abc123def4"\) | | `subscriberEmail` | string | Yes | Member email address or MD5 hash of the lowercase email | -| `tags` | string | Yes | JSON array of tag objects \(e.g., \[\{"name": "VIP", "status": "active"\}\]\) | +| `tags` | string | Yes | JSON array of tag objects \(e.g., \[\{"name": "VIP", "status": "active"\}\]\) or a comma-separated list of tag names; status defaults to "active" | #### Output @@ -1161,7 +1184,7 @@ Remove tags from a member in a Mailchimp audience | --------- | ---- | -------- | ----------- | | `listId` | string | Yes | The unique ID for the audience/list \(e.g., "abc123def4"\) | | `subscriberEmail` | string | Yes | Member email address or MD5 hash of the lowercase email | -| `tags` | string | Yes | JSON array of tag objects with inactive status \(e.g., \[\{"name": "VIP", "status": "inactive"\}\]\) | +| `tags` | string | Yes | JSON array of tag objects with inactive status \(e.g., \[\{"name": "VIP", "status": "inactive"\}\]\) or a comma-separated list of tag names; status defaults to "inactive" | #### Output @@ -1604,7 +1627,7 @@ Create a new landing page in Mailchimp | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `landingPageType` | string | Yes | The type of landing page: "signup" | +| `landingPageType` | string | Yes | The type of landing page: "signup" or "product" \("click-through" is kept for saved workflows but is not in the current API\) | | `landingPageTitle` | string | No | The title of the landing page \(e.g., "Join Our Newsletter"\) | #### Output @@ -1766,7 +1789,7 @@ Create a new batch operation in Mailchimp | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `operations` | string | Yes | JSON array of batch operations \(e.g., \[\{"method": "POST", "path": "/lists/\{list_id\}/members", "body": "..."\}\]\) | +| `operations` | json | Yes | JSON array of batch operations \(e.g., \[\{"method": "POST", "path": "/lists/\{list_id\}/members", "body": \{"email_address": "user@example.com", "status": "subscribed"\}\}\]\); an object body is sent as the JSON string Mailchimp expects | #### Output diff --git a/apps/docs/content/docs/integrations/monday.mdx b/apps/docs/content/docs/integrations/monday.mdx index 5696050f053..38ae73a6e1f 100644 --- a/apps/docs/content/docs/integrations/monday.mdx +++ b/apps/docs/content/docs/integrations/monday.mdx @@ -158,6 +158,7 @@ Get items from a Monday.com board | `boardId` | string | Yes | The ID of the board to get items from | | `groupId` | string | No | Filter items by group ID | | `limit` | number | No | Maximum number of items to return \(default 25, max 500\) | +| `cursor` | string | No | Pagination cursor from a previous Get Items response | #### Output @@ -179,6 +180,7 @@ Get items from a Monday.com board | ↳ `updatedAt` | string | Last updated timestamp | | ↳ `url` | string | Item URL | | `count` | number | Number of items returned | +| `cursor` | string | Pagination cursor for fetching the next page \(null when there are no more items\) | diff --git a/apps/docs/content/docs/integrations/notion.mdx b/apps/docs/content/docs/integrations/notion.mdx index 35ef4a70f03..6800b3a8b51 100644 --- a/apps/docs/content/docs/integrations/notion.mdx +++ b/apps/docs/content/docs/integrations/notion.mdx @@ -46,6 +46,7 @@ Read content from a Notion page | Parameter | Type | Description | | --------- | ---- | ----------- | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `url` | string | Notion page URL | | `created_time` | string | ISO 8601 creation timestamp | | `last_edited_time` | string | ISO 8601 last edit timestamp | @@ -338,6 +339,7 @@ Append new block children (content) to a Notion page or block | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -377,6 +379,7 @@ Retrieve a single Notion block by its UUID, including its type-specific content | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -418,6 +421,7 @@ Retrieve the block children (content) of a Notion page or block | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -459,6 +463,7 @@ Update the content or archived state of a single Notion block | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -498,6 +503,7 @@ Delete (move to trash) a single Notion block | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -539,6 +545,7 @@ Create a comment on a Notion page or within an existing discussion thread | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -580,6 +587,7 @@ List unresolved comments on a Notion page or block | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -620,6 +628,7 @@ List all users (members and bots) in the Notion workspace | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | @@ -659,6 +668,7 @@ Retrieve a single Notion user by their UUID | Parameter | Type | Description | | --------- | ---- | ----------- | | `content` | string | Page content in markdown format, or comment text for create comment | +| `truncated` | boolean | True when the page has more than 1000 top-level blocks and content stops there | | `title` | string | Page or database title | | `url` | string | Notion URL | | `id` | string | Page, database, block, comment, or user ID | diff --git a/apps/docs/content/docs/integrations/oracle_fusion_financials.mdx b/apps/docs/content/docs/integrations/oracle_fusion_financials.mdx index 179b1bbb4ff..e389d0fadc3 100644 --- a/apps/docs/content/docs/integrations/oracle_fusion_financials.mdx +++ b/apps/docs/content/docs/integrations/oracle_fusion_financials.mdx @@ -3,7 +3,7 @@ title: Oracle Fusion Financials description: Manage receivables and expenses, and report on payables and ledgers integration: type: "oracle_fusion_financials" - color: "#C74634" + color: "#FFFFFF" --- import { ReferenceDetails } from "@/components/ui/reference-details" diff --git a/apps/docs/content/docs/integrations/pipedrive.mdx b/apps/docs/content/docs/integrations/pipedrive.mdx index 513c5812499..0a045ec21f8 100644 --- a/apps/docs/content/docs/integrations/pipedrive.mdx +++ b/apps/docs/content/docs/integrations/pipedrive.mdx @@ -186,6 +186,7 @@ Retrieve files from Pipedrive with optional filters | `files` | array | Array of file objects from Pipedrive | | ↳ `id` | number | File ID | | ↳ `name` | string | File name | +| ↳ `file_name` | string | Stored file name | | ↳ `file_type` | string | File type/extension | | ↳ `file_size` | number | File size in bytes | | ↳ `add_time` | string | When the file was uploaded | @@ -263,7 +264,8 @@ Retrieve all pipelines from Pipedrive | `sort_by` | string | No | Field to sort by: id, update_time, add_time \(default: id\) | | `sort_direction` | string | No | Sorting direction: asc, desc \(default: asc\) | | `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) | -| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) | +| `start` | string | No | Ignored: Pipedrive API v2 pages pipelines with cursor instead of an offset | +| `cursor` | string | No | For pagination, the marker representing the first item on the next page | #### Output @@ -272,15 +274,15 @@ Retrieve all pipelines from Pipedrive | `pipelines` | array | Array of pipeline objects from Pipedrive | | ↳ `id` | number | Pipeline ID | | ↳ `name` | string | Pipeline name | -| ↳ `url_title` | string | URL-friendly title | | ↳ `order_nr` | number | Pipeline order number | -| ↳ `active` | boolean | Whether the pipeline is active | -| ↳ `deal_probability` | boolean | Whether deal probability is enabled | -| ↳ `add_time` | string | When the pipeline was created | -| ↳ `update_time` | string | When the pipeline was last updated | +| ↳ `is_deleted` | boolean | Whether the pipeline is deleted | +| ↳ `is_deal_probability_enabled` | boolean | Whether deal probability is enabled | +| ↳ `add_time` | string | When the pipeline was created \(ISO 8601\) | +| ↳ `update_time` | string | When the pipeline was last updated \(ISO 8601\) | | `total_items` | number | Total number of pipelines returned | | `has_more` | boolean | Whether more pipelines are available | -| `next_start` | number | Offset for fetching the next page | +| `next_start` | number | Always null: API v2 pages with next_cursor instead of an offset | +| `next_cursor` | string | Cursor for fetching the next page; null on the last page | | `success` | boolean | Operation success status | @@ -321,7 +323,7 @@ Retrieve all projects or a specific project from Pipedrive | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `project_id` | string | No | Optional: ID of a specific project to retrieve \(e.g., "123"\) | -| `status` | string | No | Filter by project status: open, completed, deleted \(only for listing all\) | +| `status` | string | No | Filter by project status: open, completed, canceled, deleted \(only for listing all\) | | `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500, only for listing all\) | | `cursor` | string | No | For pagination, the marker representing the first item on the next page | @@ -419,7 +421,7 @@ Create a new activity (task) in Pipedrive | `due_time` | string | No | Due time in HH:MM format \(e.g., "14:30"\) | | `duration` | string | No | Duration in HH:MM format \(e.g., "01:00" for 1 hour\) | | `deal_id` | string | No | ID of the deal to associate with \(e.g., "123"\) | -| `person_id` | string | No | ID of the person to associate with \(e.g., "456"\) | +| `person_id` | string | No | ID of the person to associate with as the primary participant \(e.g., "456"\) | | `org_id` | string | No | ID of the organization to associate with \(e.g., "789"\) | | `note` | string | No | Notes for the activity | @@ -533,7 +535,7 @@ Create a new lead in Pipedrive | `value_amount` | string | No | Potential value amount \(e.g., "10000"\) | | `value_currency` | string | No | Currency code \(e.g., "USD", "EUR", "GBP"\) | | `expected_close_date` | string | No | Expected close date in YYYY-MM-DD format \(e.g., "2025-04-15"\) | -| `visible_to` | string | No | Visibility: 1 \(Owner & followers\), 3 \(Entire company\) | +| `visible_to` | string | No | Visibility group; meanings depend on the Pipedrive plan. Light, Growth, and Professional: 1 = owner & followers, 3 = entire company. Premium and Ultimate: 1 = owner only, 3 = owner's visibility group, 5 = owner's visibility group and sub-groups, 7 = entire company | #### Output diff --git a/apps/docs/content/docs/integrations/sap_s4hana.mdx b/apps/docs/content/docs/integrations/sap_s4hana.mdx index d57aa7c8d7b..2d8e94ef82a 100644 --- a/apps/docs/content/docs/integrations/sap_s4hana.mdx +++ b/apps/docs/content/docs/integrations/sap_s4hana.mdx @@ -72,7 +72,7 @@ List business partners from SAP S/4HANA Cloud (API_BUSINESS_PARTNER, A_BusinessP | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 envelope `\{ d: \{ results: \[...\], __count?, __next? \} \}`. Properties listed below describe each element of `data.d.results`. | +| `data` | json | Array of A_BusinessPartner entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | | ↳ `BusinessPartner` | string | Business partner key \(up to 10 chars\) | | ↳ `BusinessPartnerFullName` | string | Full name \(concatenated first/last or organization name\) | | ↳ `BusinessPartnerCategory` | string | "1" Person, "2" Organization, "3" Group | @@ -120,7 +120,7 @@ Retrieve a single business partner by BusinessPartner key from SAP S/4HANA Cloud | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | A_BusinessPartner entity \(under d in OData v2\) | +| `data` | json | A_BusinessPartner entity \(the integration unwraps the OData v2 `d` envelope\) | | ↳ `BusinessPartner` | string | Business partner key \(up to 10 chars\) | | ↳ `BusinessPartnerFullName` | string | Full name \(concatenated first/last or organization name\) | | ↳ `BusinessPartnerCategory` | string | "1" Person, "2" Organization, "3" Group | @@ -173,7 +173,7 @@ Create a business partner in SAP S/4HANA Cloud (API_BUSINESS_PARTNER, A_Business | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(201 on success\) | -| `data` | json | Created A_BusinessPartner entity \(under d in OData v2\) | +| `data` | json | Created A_BusinessPartner entity \(the integration unwraps the OData v2 `d` envelope\) | | ↳ `BusinessPartner` | string | Generated business partner key \(up to 10 chars\) | | ↳ `BusinessPartnerFullName` | string | Full name \(concatenated first/last or organization name\) | | ↳ `BusinessPartnerCategory` | string | "1" Person, "2" Organization, "3" Group | @@ -419,47 +419,43 @@ List suppliers from SAP S/4HANA Cloud (API_BUSINESS_PARTNER, A_Supplier) with op | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_Supplier entities | -| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | -| ↳ `AlternativePayeeAccountNumber` | string | Account number of the alternative payee | -| ↳ `AuthorizationGroup` | string | Authorization group | -| ↳ `BusinessPartner` | string | Linked BusinessPartner key | -| ↳ `BR_TaxIsSplit` | boolean | Brazil-specific tax split flag | -| ↳ `CreatedByUser` | string | User who created the supplier | -| ↳ `CreationDate` | string | Creation date \(OData v2 epoch\) | -| ↳ `Customer` | string | Linked customer key \(if any\) | -| ↳ `DeletionIndicator` | boolean | Central deletion flag | -| ↳ `BirthDate` | string | Date of birth \(OData v2 epoch\) | -| ↳ `ConcatenatedInternationalLocNo` | string | Concatenated international location number | -| ↳ `FiscalAddress` | string | Fiscal address number | -| ↳ `Industry` | string | Industry key | -| ↳ `InternationalLocationNumber1` | string | International location number, part 1 | -| ↳ `InternationalLocationNumber2` | string | International location number, part 2 | -| ↳ `InternationalLocationNumber3` | string | International location number, part 3 | -| ↳ `IsNaturalPerson` | boolean | Indicates whether the supplier is a natural person | -| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | -| ↳ `PostingIsBlocked` | boolean | Posting block flag | -| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | -| ↳ `ResponsibleType` | string | Type of business \(Brazil\) | -| ↳ `SupplierAccountGroup` | string | Supplier account group | -| ↳ `SupplierCorporateGroup` | string | Corporate group identifier | -| ↳ `SupplierFullName` | string | Full name of the supplier | -| ↳ `SupplierName` | string | Supplier name | -| ↳ `SupplierProcurementBlock` | string | Procurement block at supplier level | -| ↳ `SuplrProofOfDelivRlvtCode` | string | Proof of delivery relevance code | -| ↳ `SuplrQltyInProcmtCertfnValidTo` | string | Quality certification validity end date \(OData v2 epoch\) | -| ↳ `SuplrQualityManagementSystem` | string | Quality management system of the supplier | -| ↳ `TaxNumber1` | string | Tax number 1 | -| ↳ `TaxNumber2` | string | Tax number 2 | -| ↳ `TaxNumber3` | string | Tax number 3 | -| ↳ `TaxNumber4` | string | Tax number 4 | -| ↳ `TaxNumber5` | string | Tax number 5 | -| ↳ `TaxNumberResponsible` | string | Tax number of responsible party | -| ↳ `TaxNumberType` | string | Tax number type | -| ↳ `VATRegistration` | string | VAT registration number | -| ↳ `__next` | string | OData skiptoken URL for next page | +| `data` | json | Array of A_Supplier entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | +| ↳ `AlternativePayeeAccountNumber` | string | Account number of the alternative payee | +| ↳ `AuthorizationGroup` | string | Authorization group | +| ↳ `BR_TaxIsSplit` | boolean | Brazil-specific tax split flag | +| ↳ `CreatedByUser` | string | User who created the supplier | +| ↳ `CreationDate` | string | Creation date \(OData v2 epoch\) | +| ↳ `Customer` | string | Linked customer key \(if any\) | +| ↳ `DeletionIndicator` | boolean | Central deletion flag | +| ↳ `BirthDate` | string | Date of birth \(OData v2 epoch\) | +| ↳ `ConcatenatedInternationalLocNo` | string | Concatenated international location number | +| ↳ `FiscalAddress` | string | Fiscal address number | +| ↳ `Industry` | string | Industry key | +| ↳ `InternationalLocationNumber1` | string | International location number, part 1 | +| ↳ `InternationalLocationNumber2` | string | International location number, part 2 | +| ↳ `InternationalLocationNumber3` | string | International location number, part 3 | +| ↳ `IsNaturalPerson` | string | Indicates whether the supplier is a natural person | +| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | +| ↳ `PostingIsBlocked` | boolean | Posting block flag | +| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | +| ↳ `ResponsibleType` | string | Type of business \(Brazil\) | +| ↳ `SupplierAccountGroup` | string | Supplier account group | +| ↳ `SupplierCorporateGroup` | string | Corporate group identifier | +| ↳ `SupplierFullName` | string | Full name of the supplier | +| ↳ `SupplierName` | string | Supplier name | +| ↳ `SupplierProcurementBlock` | string | Procurement block at supplier level | +| ↳ `SuplrProofOfDelivRlvtCode` | string | Proof of delivery relevance code | +| ↳ `SuplrQltyInProcmtCertfnValidTo` | string | Quality certification validity end date \(OData v2 epoch\) | +| ↳ `SuplrQualityManagementSystem` | string | Quality management system of the supplier | +| ↳ `TaxNumber1` | string | Tax number 1 | +| ↳ `TaxNumber2` | string | Tax number 2 | +| ↳ `TaxNumber3` | string | Tax number 3 | +| ↳ `TaxNumber4` | string | Tax number 4 | +| ↳ `TaxNumber5` | string | Tax number 5 | +| ↳ `TaxNumberResponsible` | string | Tax number of responsible party | +| ↳ `TaxNumberType` | string | Tax number type | +| ↳ `VATRegistration` | string | VAT registration number | @@ -492,45 +488,43 @@ Retrieve a single supplier by Supplier key from SAP S/4HANA Cloud (API_BUSINESS_ | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_Supplier entity | -| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | -| ↳ `AlternativePayeeAccountNumber` | string | Account number of the alternative payee | -| ↳ `AuthorizationGroup` | string | Authorization group | -| ↳ `BusinessPartner` | string | Linked BusinessPartner key | -| ↳ `BR_TaxIsSplit` | boolean | Brazil-specific tax split flag | -| ↳ `CreatedByUser` | string | User who created the supplier | -| ↳ `CreationDate` | string | Creation date \(OData v2 epoch\) | -| ↳ `Customer` | string | Linked customer key \(if any\) | -| ↳ `DeletionIndicator` | boolean | Central deletion flag | -| ↳ `BirthDate` | string | Date of birth \(OData v2 epoch\) | -| ↳ `ConcatenatedInternationalLocNo` | string | Concatenated international location number | -| ↳ `FiscalAddress` | string | Fiscal address number | -| ↳ `Industry` | string | Industry key | -| ↳ `InternationalLocationNumber1` | string | International location number, part 1 | -| ↳ `InternationalLocationNumber2` | string | International location number, part 2 | -| ↳ `InternationalLocationNumber3` | string | International location number, part 3 | -| ↳ `IsNaturalPerson` | boolean | Indicates whether the supplier is a natural person | -| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | -| ↳ `PostingIsBlocked` | boolean | Posting block flag | -| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | -| ↳ `ResponsibleType` | string | Type of business \(Brazil\) | -| ↳ `SupplierAccountGroup` | string | Supplier account group | -| ↳ `SupplierCorporateGroup` | string | Corporate group identifier | -| ↳ `SupplierFullName` | string | Full name of the supplier | -| ↳ `SupplierName` | string | Supplier name | -| ↳ `SupplierProcurementBlock` | string | Procurement block at supplier level | -| ↳ `SuplrProofOfDelivRlvtCode` | string | Proof of delivery relevance code | -| ↳ `SuplrQltyInProcmtCertfnValidTo` | string | Quality certification validity end date \(OData v2 epoch\) | -| ↳ `SuplrQualityManagementSystem` | string | Quality management system of the supplier | -| ↳ `TaxNumber1` | string | Tax number 1 | -| ↳ `TaxNumber2` | string | Tax number 2 | -| ↳ `TaxNumber3` | string | Tax number 3 | -| ↳ `TaxNumber4` | string | Tax number 4 | -| ↳ `TaxNumber5` | string | Tax number 5 | -| ↳ `TaxNumberResponsible` | string | Tax number of responsible party | -| ↳ `TaxNumberType` | string | Tax number type | -| ↳ `VATRegistration` | string | VAT registration number | +| `data` | json | A_Supplier entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | +| ↳ `AlternativePayeeAccountNumber` | string | Account number of the alternative payee | +| ↳ `AuthorizationGroup` | string | Authorization group | +| ↳ `BR_TaxIsSplit` | boolean | Brazil-specific tax split flag | +| ↳ `CreatedByUser` | string | User who created the supplier | +| ↳ `CreationDate` | string | Creation date \(OData v2 epoch\) | +| ↳ `Customer` | string | Linked customer key \(if any\) | +| ↳ `DeletionIndicator` | boolean | Central deletion flag | +| ↳ `BirthDate` | string | Date of birth \(OData v2 epoch\) | +| ↳ `ConcatenatedInternationalLocNo` | string | Concatenated international location number | +| ↳ `FiscalAddress` | string | Fiscal address number | +| ↳ `Industry` | string | Industry key | +| ↳ `InternationalLocationNumber1` | string | International location number, part 1 | +| ↳ `InternationalLocationNumber2` | string | International location number, part 2 | +| ↳ `InternationalLocationNumber3` | string | International location number, part 3 | +| ↳ `IsNaturalPerson` | string | Indicates whether the supplier is a natural person | +| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | +| ↳ `PostingIsBlocked` | boolean | Posting block flag | +| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | +| ↳ `ResponsibleType` | string | Type of business \(Brazil\) | +| ↳ `SupplierAccountGroup` | string | Supplier account group | +| ↳ `SupplierCorporateGroup` | string | Corporate group identifier | +| ↳ `SupplierFullName` | string | Full name of the supplier | +| ↳ `SupplierName` | string | Supplier name | +| ↳ `SupplierProcurementBlock` | string | Procurement block at supplier level | +| ↳ `SuplrProofOfDelivRlvtCode` | string | Proof of delivery relevance code | +| ↳ `SuplrQltyInProcmtCertfnValidTo` | string | Quality certification validity end date \(OData v2 epoch\) | +| ↳ `SuplrQualityManagementSystem` | string | Quality management system of the supplier | +| ↳ `TaxNumber1` | string | Tax number 1 | +| ↳ `TaxNumber2` | string | Tax number 2 | +| ↳ `TaxNumber3` | string | Tax number 3 | +| ↳ `TaxNumber4` | string | Tax number 4 | +| ↳ `TaxNumber5` | string | Tax number 5 | +| ↳ `TaxNumberResponsible` | string | Tax number of responsible party | +| ↳ `TaxNumberType` | string | Tax number type | +| ↳ `VATRegistration` | string | VAT registration number | @@ -563,16 +557,14 @@ Update fields on an A_Supplier entity in SAP S/4HANA Cloud (API_BUSINESS_PARTNER | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(204 on success\) | -| `data` | json | Null on 204 success, or OData v2 envelope with updated entity at output.data.d when SAP returns a representation | -| ↳ `d` | json | A_Supplier entity \(when SAP returns a representation\) | -| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | -| ↳ `SupplierName` | string | Supplier name | -| ↳ `SupplierAccountGroup` | string | Supplier account group | -| ↳ `BusinessPartner` | string | Linked BusinessPartner key | -| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | -| ↳ `PostingIsBlocked` | boolean | Posting block flag | -| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | -| ↳ `DeletionIndicator` | boolean | Central deletion flag | +| `data` | json | Null on 204 No Content \(the usual response\), or the updated A_Supplier entity when SAP returns one | +| ↳ `Supplier` | string | Supplier key \(up to 10 characters\) | +| ↳ `SupplierName` | string | Supplier name | +| ↳ `SupplierAccountGroup` | string | Supplier account group | +| ↳ `PaymentIsBlockedForSupplier` | boolean | Payment block flag | +| ↳ `PostingIsBlocked` | boolean | Posting block flag | +| ↳ `PurchasingIsBlocked` | boolean | Purchasing block flag | +| ↳ `DeletionIndicator` | boolean | Central deletion flag | @@ -608,26 +600,23 @@ List sales orders from SAP S/4HANA Cloud (API_SALES_ORDER_SRV, A_SalesOrder) wit | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_SalesOrder entities | -| ↳ `SalesOrder` | string | Sales order number | -| ↳ `SalesOrderType` | string | Sales document type \(e.g., OR\) | -| ↳ `SalesOrganization` | string | Sales organization | -| ↳ `DistributionChannel` | string | Distribution channel | -| ↳ `OrganizationDivision` | string | Division | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `TotalNetAmount` | string | Total net amount | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `SalesOrderDate` | string | Sales order date \(OData /Date\(ms\)/\) | -| ↳ `RequestedDeliveryDate` | string | Requested delivery date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | -| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | -| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | -| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | -| ↳ `OverallSDDocumentRejectionSts` | string | Overall sales document rejection status | -| ↳ `__next` | string | OData skiptoken URL for next page | +| `data` | json | Array of A_SalesOrder entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `SalesOrder` | string | Sales order number | +| ↳ `SalesOrderType` | string | Sales document type \(e.g., OR\) | +| ↳ `SalesOrganization` | string | Sales organization | +| ↳ `DistributionChannel` | string | Distribution channel | +| ↳ `OrganizationDivision` | string | Division | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `TotalNetAmount` | string | Total net amount | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `SalesOrderDate` | string | Sales order date \(OData /Date\(ms\)/\) | +| ↳ `RequestedDeliveryDate` | string | Requested delivery date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | +| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | +| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | +| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | +| ↳ `OverallSDDocumentRejectionSts` | string | Overall sales document rejection status | @@ -660,27 +649,26 @@ Retrieve a single sales order by SalesOrder key from SAP S/4HANA Cloud (API_SALE | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_SalesOrder entity | -| ↳ `SalesOrder` | string | Sales order number | -| ↳ `SalesOrderType` | string | Sales document type | -| ↳ `SalesOrganization` | string | Sales organization | -| ↳ `DistributionChannel` | string | Distribution channel | -| ↳ `OrganizationDivision` | string | Division | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | -| ↳ `SalesOrderDate` | string | Sales order date \(OData /Date\(ms\)/\) | -| ↳ `RequestedDeliveryDate` | string | Requested delivery date \(OData /Date\(ms\)/\) | -| ↳ `PricingDate` | string | Pricing date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDateTime` | string | Last change timestamp \(OData /Date\(ms\)/\) | -| ↳ `TotalNetAmount` | string | Total net amount | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `CreationDate` | string | Creation date | -| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | -| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | -| ↳ `OverallSDDocumentRejectionSts` | string | Overall sales document rejection status | -| ↳ `to_Item` | json | Sales order items \(when $expand=to_Item\) | +| `data` | json | A_SalesOrder entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `SalesOrder` | string | Sales order number | +| ↳ `SalesOrderType` | string | Sales document type | +| ↳ `SalesOrganization` | string | Sales organization | +| ↳ `DistributionChannel` | string | Distribution channel | +| ↳ `OrganizationDivision` | string | Division | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | +| ↳ `SalesOrderDate` | string | Sales order date \(OData /Date\(ms\)/\) | +| ↳ `RequestedDeliveryDate` | string | Requested delivery date \(OData /Date\(ms\)/\) | +| ↳ `PricingDate` | string | Pricing date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDateTime` | string | Last change timestamp \(OData /Date\(ms\)/\) | +| ↳ `TotalNetAmount` | string | Total net amount | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date | +| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | +| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | +| ↳ `OverallSDDocumentRejectionSts` | string | Overall sales document rejection status | +| ↳ `to_Item` | json | Sales order items \(when $expand=to_Item\) | @@ -717,20 +705,19 @@ Create a sales order in SAP S/4HANA Cloud (API_SALES_ORDER_SRV, A_SalesOrder) wi | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(201 on create\) | -| `data` | json | OData v2 response envelope; created entity at output.data.d | -| ↳ `d` | json | Created A_SalesOrder entity | -| ↳ `SalesOrder` | string | Newly assigned sales order number | -| ↳ `SalesOrderType` | string | Sales document type | -| ↳ `SalesOrganization` | string | Sales organization | -| ↳ `DistributionChannel` | string | Distribution channel | -| ↳ `OrganizationDivision` | string | Division | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `TotalNetAmount` | string | Total net amount | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `CreationDate` | string | Creation date | -| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | -| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | -| ↳ `to_Item` | json | Deep-inserted sales order items as returned by SAP | +| `data` | json | Created A_SalesOrder entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `SalesOrder` | string | Newly assigned sales order number | +| ↳ `SalesOrderType` | string | Sales document type | +| ↳ `SalesOrganization` | string | Sales organization | +| ↳ `DistributionChannel` | string | Distribution channel | +| ↳ `OrganizationDivision` | string | Division | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `TotalNetAmount` | string | Total net amount | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date | +| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | +| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | +| ↳ `to_Item` | json | Deep-inserted sales order items as returned by SAP | @@ -763,13 +750,12 @@ Update fields on an A_SalesOrder header in SAP S/4HANA Cloud (API_SALES_ORDER_SR | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(204 on success\) | -| `data` | json | Null on 204 success; otherwise OData v2 envelope with the updated entity at output.data.d | -| ↳ `d` | json | Updated A_SalesOrder entity \(when SAP returns one\) | -| ↳ `SalesOrder` | string | Sales order number | -| ↳ `SalesOrderType` | string | Sales document type | -| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | -| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | -| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | +| `data` | json | Null on 204 No Content \(the usual response\), or the updated A_SalesOrder entity when SAP returns one | +| ↳ `SalesOrder` | string | Sales order number | +| ↳ `SalesOrderType` | string | Sales document type | +| ↳ `PurchaseOrderByCustomer` | string | Customer purchase order reference | +| ↳ `OverallSDProcessStatus` | string | Overall sales document process status | +| ↳ `OverallTotalDeliveryStatus` | string | Overall total delivery status | @@ -825,7 +811,7 @@ List outbound deliveries from SAP S/4HANA Cloud (API_OUTBOUND_DELIVERY_SRV;v=000 | `tokenUrl` | string | No | OAuth token URL \(Cloud Private / On-Premise + OAuth\) | | `username` | string | No | Username for HTTP Basic auth | | `password` | string | No | Password for HTTP Basic auth | -| `filter` | string | No | OData $filter expression \(e.g., "OverallDeliveryStatus eq 'C'"\) | +| `filter` | string | No | OData $filter expression \(e.g., "OverallGoodsMovementStatus eq 'C'"\) | | `top` | number | No | Maximum results to return \($top\) | | `skip` | number | No | Number of results to skip \($skip\) | | `orderBy` | string | No | OData $orderby expression | @@ -837,27 +823,23 @@ List outbound deliveries from SAP S/4HANA Cloud (API_OUTBOUND_DELIVERY_SRV;v=000 | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_OutbDeliveryHeader entities | -| ↳ `DeliveryDocument` | string | Outbound delivery number | -| ↳ `DeliveryDocumentType` | string | Delivery document type \(e.g., LF\) | -| ↳ `SDDocumentCategory` | string | SD document category \(e.g., J = outbound delivery\) | -| ↳ `ShippingPoint` | string | Shipping point | -| ↳ `ShippingType` | string | Shipping type | -| ↳ `ShipToParty` | string | Ship-to business partner | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | -| ↳ `ActualGoodsMovementDate` | string | Actual goods issue date \(Edm.DateTime\) | -| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue date \(Edm.DateTime\) | -| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | -| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | -| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | -| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | -| ↳ `__next` | string | OData skiptoken URL for next page | -| ↳ `__count` | string | Total count when $inlinecount=allpages is used | +| `data` | json | Array of A_OutbDeliveryHeader entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `DeliveryDocument` | string | Outbound delivery number | +| ↳ `DeliveryDocumentType` | string | Delivery document type \(e.g., LF\) | +| ↳ `SDDocumentCategory` | string | SD document category \(e.g., J = outbound delivery\) | +| ↳ `ShippingPoint` | string | Shipping point | +| ↳ `ShippingType` | string | Shipping type | +| ↳ `ShipToParty` | string | Ship-to business partner | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | +| ↳ `ActualGoodsMovementDate` | string | Actual goods issue date \(Edm.DateTime\) | +| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue date \(Edm.DateTime\) | +| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | +| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | +| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | +| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | @@ -890,26 +872,25 @@ Retrieve a single outbound delivery by DeliveryDocument key from SAP S/4HANA Clo | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_OutbDeliveryHeader entity | -| ↳ `DeliveryDocument` | string | Outbound delivery number | -| ↳ `DeliveryDocumentType` | string | Delivery document type | -| ↳ `SDDocumentCategory` | string | SD document category \(e.g., J = outbound delivery\) | -| ↳ `ShippingPoint` | string | Shipping point | -| ↳ `ShippingType` | string | Shipping type | -| ↳ `ShipToParty` | string | Ship-to business partner | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | -| ↳ `ActualGoodsMovementDate` | string | Actual goods issue date \(Edm.DateTime\) | -| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue date \(Edm.DateTime\) | -| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | -| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | -| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | -| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | -| ↳ `to_DeliveryDocumentItem` | json | Delivery items \(when $expand=to_DeliveryDocumentItem\) | -| ↳ `to_DeliveryDocumentPartner` | json | Delivery partners \(when $expand=to_DeliveryDocumentPartner\) | +| `data` | json | A_OutbDeliveryHeader entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `DeliveryDocument` | string | Outbound delivery number | +| ↳ `DeliveryDocumentType` | string | Delivery document type | +| ↳ `SDDocumentCategory` | string | SD document category \(e.g., J = outbound delivery\) | +| ↳ `ShippingPoint` | string | Shipping point | +| ↳ `ShippingType` | string | Shipping type | +| ↳ `ShipToParty` | string | Ship-to business partner | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | +| ↳ `ActualGoodsMovementDate` | string | Actual goods issue date \(Edm.DateTime\) | +| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue date \(Edm.DateTime\) | +| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | +| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | +| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | +| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | +| ↳ `to_DeliveryDocumentItem` | json | Delivery items \(when $expand=to_DeliveryDocumentItem\) | +| ↳ `to_DeliveryDocumentPartner` | json | Delivery partners \(when $expand=to_DeliveryDocumentPartner\) | @@ -945,25 +926,21 @@ List inbound deliveries from SAP S/4HANA Cloud (API_INBOUND_DELIVERY_SRV;v=0002, | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_InbDeliveryHeader entities | -| ↳ `DeliveryDocument` | string | Inbound delivery number | -| ↳ `DeliveryDocumentType` | string | Delivery document type \(e.g., EL\) | -| ↳ `SDDocumentCategory` | string | SD document category \(e.g., 7 = inbound delivery\) | -| ↳ `ReceivingPlant` | string | Receiving plant | -| ↳ `Supplier` | string | Supplier business partner | -| ↳ `ShipToParty` | string | Ship-to business partner | -| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | -| ↳ `ActualGoodsMovementDate` | string | Actual goods movement \(receipt\) date \(Edm.DateTime\) | -| ↳ `PlannedGoodsMovementDate` | string | Planned goods movement date \(Edm.DateTime\) | -| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | -| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | -| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | -| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | -| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | -| ↳ `__next` | string | OData skiptoken URL for next page | -| ↳ `__count` | string | Total count when $inlinecount=allpages is used | +| `data` | json | Array of A_InbDeliveryHeader entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `DeliveryDocument` | string | Inbound delivery number | +| ↳ `DeliveryDocumentType` | string | Delivery document type \(e.g., EL\) | +| ↳ `SDDocumentCategory` | string | SD document category \(e.g., 7 = inbound delivery\) | +| ↳ `ReceivingPlant` | string | Receiving plant | +| ↳ `Supplier` | string | Supplier business partner | +| ↳ `ShipToParty` | string | Ship-to business partner | +| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | +| ↳ `ActualGoodsMovementDate` | string | Actual goods movement \(receipt\) date \(Edm.DateTime\) | +| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue/receipt date \(Edm.DateTime\) | +| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | +| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | +| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | +| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | +| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | @@ -996,24 +973,23 @@ Retrieve a single inbound delivery by DeliveryDocument key from SAP S/4HANA Clou | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_InbDeliveryHeader entity | -| ↳ `DeliveryDocument` | string | Inbound delivery number | -| ↳ `DeliveryDocumentType` | string | Delivery document type | -| ↳ `SDDocumentCategory` | string | SD document category \(e.g., 7 = inbound delivery\) | -| ↳ `ReceivingPlant` | string | Receiving plant | -| ↳ `Supplier` | string | Supplier business partner | -| ↳ `ShipToParty` | string | Ship-to business partner | -| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | -| ↳ `ActualGoodsMovementDate` | string | Actual goods movement \(receipt\) date \(Edm.DateTime\) | -| ↳ `PlannedGoodsMovementDate` | string | Planned goods movement date \(Edm.DateTime\) | -| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | -| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | -| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | -| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | -| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | -| ↳ `to_DeliveryDocumentItem` | json | Delivery items \(when $expand=to_DeliveryDocumentItem\) | -| ↳ `to_DeliveryDocumentPartner` | json | Delivery partners \(when $expand=to_DeliveryDocumentPartner\) | +| `data` | json | A_InbDeliveryHeader entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `DeliveryDocument` | string | Inbound delivery number | +| ↳ `DeliveryDocumentType` | string | Delivery document type | +| ↳ `SDDocumentCategory` | string | SD document category \(e.g., 7 = inbound delivery\) | +| ↳ `ReceivingPlant` | string | Receiving plant | +| ↳ `Supplier` | string | Supplier business partner | +| ↳ `ShipToParty` | string | Ship-to business partner | +| ↳ `DeliveryDate` | string | Delivery date \(Edm.DateTime\) | +| ↳ `ActualGoodsMovementDate` | string | Actual goods movement \(receipt\) date \(Edm.DateTime\) | +| ↳ `PlannedGoodsIssueDate` | string | Planned goods issue/receipt date \(Edm.DateTime\) | +| ↳ `OverallSDProcessStatus` | string | Overall SD process \(delivery\) status | +| ↳ `OverallGoodsMovementStatus` | string | Overall goods movement status | +| ↳ `DocumentDate` | string | Document date \(Edm.DateTime\) | +| ↳ `CreationDate` | string | Creation date \(Edm.DateTime\) | +| ↳ `LastChangeDate` | string | Last change date \(Edm.DateTime\) | +| ↳ `to_DeliveryDocumentItem` | json | Delivery items \(when $expand=to_DeliveryDocumentItem\) | +| ↳ `to_DeliveryDocumentPartner` | json | Delivery partners \(when $expand=to_DeliveryDocumentPartner\) | @@ -1049,40 +1025,36 @@ List billing documents (customer invoices) from SAP S/4HANA Cloud (API_BILLING_D | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_BillingDocument entities | -| ↳ `BillingDocument` | string | Billing document number | -| ↳ `SDDocumentCategory` | string | SD document category | -| ↳ `BillingDocumentCategory` | string | Billing document category | -| ↳ `BillingDocumentType` | string | Billing document type \(e.g., F2\) | -| ↳ `BillingDocumentDate` | string | Billing document date \(OData /Date\(ms\)/\) | -| ↳ `BillingDocumentIsCancelled` | boolean | Whether the billing document is cancelled | -| ↳ `CancelledBillingDocument` | string | Cancelled billing document number | -| ↳ `TotalNetAmount` | string | Total net amount \(Edm.Decimal as string\) | -| ↳ `TaxAmount` | string | Tax amount \(Edm.Decimal as string\) | -| ↳ `TotalGrossAmount` | string | Total gross amount \(Edm.Decimal as string\) | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `PayerParty` | string | Payer party | -| ↳ `SalesOrganization` | string | Sales organization | -| ↳ `DistributionChannel` | string | Distribution channel | -| ↳ `Division` | string | Division | -| ↳ `CompanyCode` | string | Company code | -| ↳ `FiscalYear` | string | Fiscal year | -| ↳ `OverallBillingStatus` | string | Overall billing status | -| ↳ `AccountingPostingStatus` | string | Accounting posting status | -| ↳ `AccountingTransferStatus` | string | Accounting transfer status | -| ↳ `InvoiceClearingStatus` | string | Invoice clearing status | -| ↳ `AccountingDocument` | string | Linked accounting document | -| ↳ `CustomerPaymentTerms` | string | Customer payment terms | -| ↳ `PaymentMethod` | string | Payment method | -| ↳ `DocumentReferenceID` | string | Document reference ID | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDateTime` | string | Last change date-time \(Edm.DateTimeOffset\) | -| ↳ `__next` | string | OData skiptoken URL for next page | -| ↳ `__count` | string | Total count when $inlinecount=allpages is used | +| `data` | json | Array of A_BillingDocument entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `BillingDocument` | string | Billing document number | +| ↳ `SDDocumentCategory` | string | SD document category | +| ↳ `BillingDocumentCategory` | string | Billing document category | +| ↳ `BillingDocumentType` | string | Billing document type \(e.g., F2\) | +| ↳ `BillingDocumentDate` | string | Billing document date \(OData /Date\(ms\)/\) | +| ↳ `BillingDocumentIsCancelled` | boolean | Whether the billing document is cancelled | +| ↳ `CancelledBillingDocument` | string | Cancelled billing document number | +| ↳ `TotalNetAmount` | string | Total net amount \(Edm.Decimal as string\) | +| ↳ `TaxAmount` | string | Tax amount \(Edm.Decimal as string\) | +| ↳ `TotalGrossAmount` | string | Total gross amount \(Edm.Decimal as string\) | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `PayerParty` | string | Payer party | +| ↳ `SalesOrganization` | string | Sales organization | +| ↳ `DistributionChannel` | string | Distribution channel | +| ↳ `Division` | string | Division | +| ↳ `CompanyCode` | string | Company code | +| ↳ `FiscalYear` | string | Fiscal year | +| ↳ `OverallBillingStatus` | string | Overall billing status | +| ↳ `AccountingPostingStatus` | string | Accounting posting status | +| ↳ `AccountingTransferStatus` | string | Accounting transfer status | +| ↳ `InvoiceClearingStatus` | string | Invoice clearing status | +| ↳ `AccountingDocument` | string | Linked accounting document | +| ↳ `CustomerPaymentTerms` | string | Customer payment terms | +| ↳ `PaymentMethod` | string | Payment method | +| ↳ `DocumentReferenceID` | string | Document reference ID | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDateTime` | string | Last change date-time \(Edm.DateTimeOffset\) | @@ -1115,40 +1087,39 @@ Retrieve a single billing document (customer invoice) by BillingDocument key fro | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_BillingDocument entity | -| ↳ `BillingDocument` | string | Billing document number | -| ↳ `SDDocumentCategory` | string | SD document category | -| ↳ `BillingDocumentCategory` | string | Billing document category | -| ↳ `BillingDocumentType` | string | Billing document type | -| ↳ `BillingDocumentDate` | string | Billing document date \(OData /Date\(ms\)/\) | -| ↳ `BillingDocumentIsCancelled` | boolean | Whether the billing document is cancelled | -| ↳ `CancelledBillingDocument` | string | Cancelled billing document number | -| ↳ `TotalNetAmount` | string | Total net amount \(Edm.Decimal as string\) | -| ↳ `TaxAmount` | string | Tax amount \(Edm.Decimal as string\) | -| ↳ `TotalGrossAmount` | string | Total gross amount \(Edm.Decimal as string\) | -| ↳ `TransactionCurrency` | string | Document currency | -| ↳ `SoldToParty` | string | Sold-to business partner | -| ↳ `PayerParty` | string | Payer party | -| ↳ `SalesOrganization` | string | Sales organization | -| ↳ `DistributionChannel` | string | Distribution channel | -| ↳ `Division` | string | Division | -| ↳ `CompanyCode` | string | Company code | -| ↳ `FiscalYear` | string | Fiscal year | -| ↳ `OverallBillingStatus` | string | Overall billing status | -| ↳ `AccountingPostingStatus` | string | Accounting posting status | -| ↳ `AccountingTransferStatus` | string | Accounting transfer status | -| ↳ `InvoiceClearingStatus` | string | Invoice clearing status | -| ↳ `AccountingDocument` | string | Linked accounting document | -| ↳ `CustomerPaymentTerms` | string | Customer payment terms | -| ↳ `PaymentMethod` | string | Payment method | -| ↳ `DocumentReferenceID` | string | Document reference ID | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | -| ↳ `LastChangeDateTime` | string | Last change date-time \(Edm.DateTimeOffset\) | -| ↳ `to_Item` | json | Billing document items \(when $expand=to_Item\) | -| ↳ `to_Partner` | json | Billing document partners \(when $expand=to_Partner\) | -| ↳ `to_PricingElement` | json | Billing document pricing elements \(when $expand=to_PricingElement\) | +| `data` | json | A_BillingDocument entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `BillingDocument` | string | Billing document number | +| ↳ `SDDocumentCategory` | string | SD document category | +| ↳ `BillingDocumentCategory` | string | Billing document category | +| ↳ `BillingDocumentType` | string | Billing document type | +| ↳ `BillingDocumentDate` | string | Billing document date \(OData /Date\(ms\)/\) | +| ↳ `BillingDocumentIsCancelled` | boolean | Whether the billing document is cancelled | +| ↳ `CancelledBillingDocument` | string | Cancelled billing document number | +| ↳ `TotalNetAmount` | string | Total net amount \(Edm.Decimal as string\) | +| ↳ `TaxAmount` | string | Tax amount \(Edm.Decimal as string\) | +| ↳ `TotalGrossAmount` | string | Total gross amount \(Edm.Decimal as string\) | +| ↳ `TransactionCurrency` | string | Document currency | +| ↳ `SoldToParty` | string | Sold-to business partner | +| ↳ `PayerParty` | string | Payer party | +| ↳ `SalesOrganization` | string | Sales organization | +| ↳ `DistributionChannel` | string | Distribution channel | +| ↳ `Division` | string | Division | +| ↳ `CompanyCode` | string | Company code | +| ↳ `FiscalYear` | string | Fiscal year | +| ↳ `OverallBillingStatus` | string | Overall billing status | +| ↳ `AccountingPostingStatus` | string | Accounting posting status | +| ↳ `AccountingTransferStatus` | string | Accounting transfer status | +| ↳ `InvoiceClearingStatus` | string | Invoice clearing status | +| ↳ `AccountingDocument` | string | Linked accounting document | +| ↳ `CustomerPaymentTerms` | string | Customer payment terms | +| ↳ `PaymentMethod` | string | Payment method | +| ↳ `DocumentReferenceID` | string | Document reference ID | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDate` | string | Last change date \(OData /Date\(ms\)/\) | +| ↳ `LastChangeDateTime` | string | Last change date-time \(Edm.DateTimeOffset\) | +| ↳ `to_Item` | json | Billing document items \(when $expand=to_Item\) | +| ↳ `to_Partner` | json | Billing document partners \(when $expand=to_Partner\) | +| ↳ `to_PricingElement` | json | Billing document pricing elements \(when $expand=to_PricingElement\) | @@ -1184,30 +1155,26 @@ List products (materials) from SAP S/4HANA Cloud (API_PRODUCT_SRV, A_Product) wi | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_Product entities | -| ↳ `Product` | string | Product \(material\) number | -| ↳ `ProductType` | string | Product type \(e.g., FERT, HAWA\) | -| ↳ `ProductGroup` | string | Material group | -| ↳ `BaseUnit` | string | Base unit of measure | -| ↳ `Brand` | string | Brand | -| ↳ `Division` | string | Division | -| ↳ `GrossWeight` | string | Gross weight | -| ↳ `NetWeight` | string | Net weight | -| ↳ `WeightUnit` | string | Weight unit of measure | -| ↳ `CrossPlantStatus` | string | Cross-plant material status | -| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | -| ↳ `ProductStandardID` | string | Standard product ID \(e.g., GTIN\) | -| ↳ `ItemCategoryGroup` | string | Item category group | -| ↳ `ProductOldID` | string | Legacy/old product ID | -| ↳ `CreatedByUser` | string | User who created the product | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `LastChangedByUser` | string | User who last changed the product | -| ↳ `LastChangeDate` | string | Last change date | -| ↳ `LastChangeDateTime` | string | Last change timestamp \(Edm.DateTimeOffset\) | -| ↳ `__next` | string | OData skiptoken URL for next page | -| ↳ `__count` | string | Total count when $inlinecount=allpages is used | +| `data` | json | Array of A_Product entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `Product` | string | Product \(material\) number | +| ↳ `ProductType` | string | Product type \(e.g., FERT, HAWA\) | +| ↳ `ProductGroup` | string | Material group | +| ↳ `BaseUnit` | string | Base unit of measure | +| ↳ `Brand` | string | Brand | +| ↳ `Division` | string | Division | +| ↳ `GrossWeight` | string | Gross weight | +| ↳ `NetWeight` | string | Net weight | +| ↳ `WeightUnit` | string | Weight unit of measure | +| ↳ `CrossPlantStatus` | string | Cross-plant material status | +| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | +| ↳ `ProductStandardID` | string | Standard product ID \(e.g., GTIN\) | +| ↳ `ItemCategoryGroup` | string | Item category group | +| ↳ `ProductOldID` | string | Legacy/old product ID | +| ↳ `CreatedByUser` | string | User who created the product | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `LastChangedByUser` | string | User who last changed the product | +| ↳ `LastChangeDate` | string | Last change date | +| ↳ `LastChangeDateTime` | string | Last change timestamp \(Edm.DateTimeOffset\) | @@ -1240,30 +1207,29 @@ Retrieve a single product (material) by Product key from SAP S/4HANA Cloud (API_ | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_Product entity | -| ↳ `Product` | string | Product \(material\) number | -| ↳ `ProductType` | string | Product type \(e.g., FERT, HAWA\) | -| ↳ `ProductGroup` | string | Material group | -| ↳ `BaseUnit` | string | Base unit of measure | -| ↳ `Brand` | string | Brand | -| ↳ `Division` | string | Division | -| ↳ `GrossWeight` | string | Gross weight | -| ↳ `NetWeight` | string | Net weight | -| ↳ `WeightUnit` | string | Weight unit of measure | -| ↳ `CrossPlantStatus` | string | Cross-plant material status | -| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | -| ↳ `ProductStandardID` | string | Standard product ID \(e.g., GTIN\) | -| ↳ `ItemCategoryGroup` | string | Item category group | -| ↳ `ProductOldID` | string | Legacy/old product ID | -| ↳ `CreatedByUser` | string | User who created the product | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `LastChangedByUser` | string | User who last changed the product | -| ↳ `LastChangeDate` | string | Last change date | -| ↳ `LastChangeDateTime` | string | Last change timestamp \(Edm.DateTimeOffset\) | -| ↳ `to_Description` | json | Product descriptions \(when $expand=to_Description\) | -| ↳ `to_Plant` | json | Plant-level data \(when $expand=to_Plant\) | -| ↳ `to_ProductSales` | json | Sales data \(when $expand=to_ProductSales\) | +| `data` | json | A_Product entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `Product` | string | Product \(material\) number | +| ↳ `ProductType` | string | Product type \(e.g., FERT, HAWA\) | +| ↳ `ProductGroup` | string | Material group | +| ↳ `BaseUnit` | string | Base unit of measure | +| ↳ `Brand` | string | Brand | +| ↳ `Division` | string | Division | +| ↳ `GrossWeight` | string | Gross weight | +| ↳ `NetWeight` | string | Net weight | +| ↳ `WeightUnit` | string | Weight unit of measure | +| ↳ `CrossPlantStatus` | string | Cross-plant material status | +| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | +| ↳ `ProductStandardID` | string | Standard product ID \(e.g., GTIN\) | +| ↳ `ItemCategoryGroup` | string | Item category group | +| ↳ `ProductOldID` | string | Legacy/old product ID | +| ↳ `CreatedByUser` | string | User who created the product | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `LastChangedByUser` | string | User who last changed the product | +| ↳ `LastChangeDate` | string | Last change date | +| ↳ `LastChangeDateTime` | string | Last change timestamp \(Edm.DateTimeOffset\) | +| ↳ `to_Description` | json | Product descriptions \(when $expand=to_Description\) | +| ↳ `to_Plant` | json | Plant-level data \(when $expand=to_Plant\) | +| ↳ `to_ProductSales` | json | Sales data \(when $expand=to_ProductSales\) | @@ -1296,14 +1262,13 @@ Update fields on an A_Product entity in SAP S/4HANA Cloud (API_PRODUCT_SRV). Use | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(204 on success\) | -| `data` | json | Null on 204 success, or OData v2 envelope with the updated A_Product entity at output.data.d | -| ↳ `d` | json | Updated A_Product entity \(only present if SAP returns a body\) | -| ↳ `Product` | string | Product \(material\) number | -| ↳ `ProductType` | string | Product type | -| ↳ `ProductGroup` | string | Material group | -| ↳ `BaseUnit` | string | Base unit of measure | -| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | -| ↳ `LastChangeDate` | string | Last change date | +| `data` | json | Null on 204 No Content \(the usual response\), or the updated A_Product entity when SAP returns one | +| ↳ `Product` | string | Product \(material\) number | +| ↳ `ProductType` | string | Product type | +| ↳ `ProductGroup` | string | Material group | +| ↳ `BaseUnit` | string | Base unit of measure | +| ↳ `IsMarkedForDeletion` | boolean | Deletion flag | +| ↳ `LastChangeDate` | string | Last change date | @@ -1339,7 +1304,7 @@ List material stock quantities from SAP S/4HANA Cloud (API_MATERIAL_STOCK_SRV, A | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData payload containing the array of A_MatlStkInAcctMod stock entries | +| `data` | json | Array of A_MatlStkInAcctMod entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | | ↳ `Material` | string | Material number | | ↳ `Plant` | string | Plant identifier | | ↳ `StorageLocation` | string | Storage location identifier | @@ -1388,7 +1353,7 @@ List material document headers (goods movements) from SAP S/4HANA Cloud (API_MAT | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData payload containing the array of A_MaterialDocumentHeader entities | +| `data` | json | Array of A_MaterialDocumentHeader entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | | ↳ `MaterialDocumentYear` | string | Material document year \(4-digit fiscal year\) | | ↳ `MaterialDocument` | string | Material document number | | ↳ `DocumentDate` | string | Document date \(OData /Date\(...\)/ string\) | @@ -1401,7 +1366,7 @@ List material document headers (goods movements) from SAP S/4HANA Cloud (API_MAT | ↳ `CreationDate` | string | Creation date \(OData /Date\(...\)/ string\) | | ↳ `CreationTime` | string | Creation time \(OData PT...S string\) | | ↳ `VersionForPrintingSlip` | string | Version for printing the goods movement slip | -| ↳ `ManualPrintIsTriggered` | boolean | Indicates whether manual print was triggered for this document | +| ↳ `ManualPrintIsTriggered` | string | Indicates whether manual print was triggered for this document | | ↳ `CtrlPostgForExtWhseMgmtSyst` | string | Control posting for external warehouse management system | | ↳ `to_MaterialDocumentItem` | json | Material document items \(only present when $expand=to_MaterialDocumentItem is supplied\) | @@ -1450,7 +1415,7 @@ Retrieve a single material document header by composite key (MaterialDocument + | ↳ `CreationDate` | string | Creation date \(OData /Date\(...\)/ string\) | | ↳ `CreationTime` | string | Creation time \(OData PT...S string\) | | ↳ `VersionForPrintingSlip` | string | Version for printing the goods movement slip | -| ↳ `ManualPrintIsTriggered` | boolean | Indicates whether manual print was triggered for this document | +| ↳ `ManualPrintIsTriggered` | string | Indicates whether manual print was triggered for this document | | ↳ `CtrlPostgForExtWhseMgmtSyst` | string | Control posting for external warehouse management system | | ↳ `to_MaterialDocumentItem` | json | Material document items \(only present when $expand=to_MaterialDocumentItem is supplied\) | @@ -1488,14 +1453,11 @@ List purchase requisitions from SAP S/4HANA Cloud (API_PURCHASEREQ_PROCESS_SRV, | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_PurchaseRequisitionHeader entities | -| ↳ `PurchaseRequisition` | string | Purchase requisition number | -| ↳ `PurchaseRequisitionType` | string | Purchase requisition document type \(e.g., NB\) | -| ↳ `PurReqnDescription` | string | Purchase requisition description | -| ↳ `SourceDetermination` | string | Source-of-supply determination flag | -| ↳ `__next` | string | OData skiptoken URL for next page | +| `data` | json | Array of A_PurchaseRequisitionHeader entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `PurchaseRequisition` | string | Purchase requisition number | +| ↳ `PurchaseRequisitionType` | string | Purchase requisition document type \(e.g., NB\) | +| ↳ `PurReqnDescription` | string | Purchase requisition description | +| ↳ `SourceDetermination` | boolean | Source-of-supply determination flag | @@ -1528,13 +1490,12 @@ Retrieve a single purchase requisition by PurchaseRequisition key from SAP S/4HA | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_PurchaseRequisitionHeader entity | -| ↳ `PurchaseRequisition` | string | Purchase requisition number | -| ↳ `PurchaseRequisitionType` | string | PR document type \(e.g., NB\) | -| ↳ `PurReqnDescription` | string | Purchase requisition description | -| ↳ `SourceDetermination` | string | Source-of-supply determination flag | -| ↳ `to_PurchaseReqnItem` | json | Expanded PR items \(when $expand=to_PurchaseReqnItem\) | +| `data` | json | A_PurchaseRequisitionHeader entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `PurchaseRequisition` | string | Purchase requisition number | +| ↳ `PurchaseRequisitionType` | string | PR document type \(e.g., NB\) | +| ↳ `PurReqnDescription` | string | Purchase requisition description | +| ↳ `SourceDetermination` | boolean | Source-of-supply determination flag | +| ↳ `to_PurchaseReqnItem` | json | Expanded PR items \(when $expand=to_PurchaseReqnItem\) | @@ -1567,13 +1528,12 @@ Create a purchase requisition in SAP S/4HANA Cloud (API_PURCHASEREQ_PROCESS_SRV, | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; created entity at output.data.d | -| ↳ `d` | json | Created A_PurchaseRequisitionHeader entity | -| ↳ `PurchaseRequisition` | string | Auto-assigned purchase requisition number | -| ↳ `PurchaseRequisitionType` | string | PR document type \(e.g., NB\) | -| ↳ `PurReqnDescription` | string | Purchase requisition description | -| ↳ `SourceDetermination` | string | Source-of-supply determination flag | -| ↳ `to_PurchaseReqnItem` | json | Created PR items returned in deep insert | +| `data` | json | Created A_PurchaseRequisitionHeader entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `PurchaseRequisition` | string | Auto-assigned purchase requisition number | +| ↳ `PurchaseRequisitionType` | string | PR document type \(e.g., NB\) | +| ↳ `PurReqnDescription` | string | Purchase requisition description | +| ↳ `SourceDetermination` | boolean | Source-of-supply determination flag | +| ↳ `to_PurchaseReqnItem` | json | Created PR items returned in deep insert | @@ -1606,12 +1566,11 @@ Update fields on an A_PurchaseRequisitionHeader entity in SAP S/4HANA Cloud (API | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(204 on success\) | -| `data` | json | Null on 204 success, or OData v2 envelope with updated A_PurchaseRequisitionHeader at output.data.d | -| ↳ `d` | json | Updated A_PurchaseRequisitionHeader entity \(if returned\) | -| ↳ `PurchaseRequisition` | string | Purchase requisition number | -| ↳ `PurchaseRequisitionType` | string | PR document type | -| ↳ `PurReqnDescription` | string | Purchase requisition description | -| ↳ `SourceDetermination` | string | Source-of-supply determination flag | +| `data` | json | Null on 204 No Content \(the usual response\), or the updated A_PurchaseRequisitionHeader entity when SAP returns one | +| ↳ `PurchaseRequisition` | string | Purchase requisition number | +| ↳ `PurchaseRequisitionType` | string | PR document type | +| ↳ `PurReqnDescription` | string | Purchase requisition description | +| ↳ `SourceDetermination` | boolean | Source-of-supply determination flag | @@ -1647,22 +1606,17 @@ List purchase orders from SAP S/4HANA Cloud (API_PURCHASEORDER_PROCESS_SRV, A_Pu | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_PurchaseOrder entities | -| ↳ `PurchaseOrder` | string | Purchase order number | -| ↳ `PurchaseOrderType` | string | PO document type \(e.g., NB\) | -| ↳ `CompanyCode` | string | Company code | -| ↳ `PurchasingOrganization` | string | Purchasing organization | -| ↳ `PurchasingGroup` | string | Purchasing group | -| ↳ `Supplier` | string | Supplier business partner key | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `NetAmount` | string | Net amount of the purchase order | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `CreatedByUser` | string | User who created the PO | -| ↳ `PurchaseOrderDate` | string | Purchase order date | -| ↳ `__next` | string | OData skiptoken URL for next page | -| ↳ `__count` | string | Total count when $inlinecount=allpages is used | +| `data` | json | Array of A_PurchaseOrder entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `PurchaseOrder` | string | Purchase order number | +| ↳ `PurchaseOrderType` | string | PO document type \(e.g., NB\) | +| ↳ `CompanyCode` | string | Company code | +| ↳ `PurchasingOrganization` | string | Purchasing organization | +| ↳ `PurchasingGroup` | string | Purchasing group | +| ↳ `Supplier` | string | Supplier business partner key | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `CreatedByUser` | string | User who created the PO | +| ↳ `PurchaseOrderDate` | string | Purchase order date | @@ -1695,25 +1649,23 @@ Retrieve a single purchase order by PurchaseOrder key from SAP S/4HANA Cloud (AP | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_PurchaseOrder entity | -| ↳ `PurchaseOrder` | string | Purchase order number | -| ↳ `PurchaseOrderType` | string | PO document type | -| ↳ `CompanyCode` | string | Company code | -| ↳ `PurchasingOrganization` | string | Purchasing organization | -| ↳ `PurchasingGroup` | string | Purchasing group | -| ↳ `Supplier` | string | Supplier business partner key | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `NetAmount` | string | Net amount of the purchase order | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `CreatedByUser` | string | User who created the PO | -| ↳ `PurchaseOrderDate` | string | Purchase order date | -| ↳ `ValidityStartDate` | string | Validity start date | -| ↳ `ValidityEndDate` | string | Validity end date | -| ↳ `IncotermsClassification` | string | Incoterms classification \(e.g., FOB\) | -| ↳ `PaymentTerms` | string | Payment terms key | -| ↳ `LastChangeDateTime` | string | Last change timestamp \(OData /Date\(ms\)/\) | -| ↳ `to_PurchaseOrderItem` | json | Expanded PO items \(when $expand=to_PurchaseOrderItem\) | +| `data` | json | A_PurchaseOrder entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `PurchaseOrder` | string | Purchase order number | +| ↳ `PurchaseOrderType` | string | PO document type | +| ↳ `CompanyCode` | string | Company code | +| ↳ `PurchasingOrganization` | string | Purchasing organization | +| ↳ `PurchasingGroup` | string | Purchasing group | +| ↳ `Supplier` | string | Supplier business partner key | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `CreatedByUser` | string | User who created the PO | +| ↳ `PurchaseOrderDate` | string | Purchase order date | +| ↳ `ValidityStartDate` | string | Validity start date | +| ↳ `ValidityEndDate` | string | Validity end date | +| ↳ `IncotermsClassification` | string | Incoterms classification \(e.g., FOB\) | +| ↳ `PaymentTerms` | string | Payment terms key | +| ↳ `LastChangeDateTime` | string | Last change timestamp \(OData /Date\(ms\)/\) | +| ↳ `to_PurchaseOrderItem` | json | Expanded PO items \(when $expand=to_PurchaseOrderItem\) | @@ -1749,18 +1701,16 @@ Create a purchase order in SAP S/4HANA Cloud (API_PURCHASEORDER_PROCESS_SRV, A_P | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; created entity at output.data.d | -| ↳ `d` | json | Created A_PurchaseOrder entity | -| ↳ `PurchaseOrder` | string | Auto-assigned purchase order number | -| ↳ `PurchaseOrderType` | string | PO document type | -| ↳ `CompanyCode` | string | Company code | -| ↳ `PurchasingOrganization` | string | Purchasing organization | -| ↳ `PurchasingGroup` | string | Purchasing group | -| ↳ `Supplier` | string | Supplier business partner key | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `NetAmount` | string | Net amount of the purchase order | -| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | -| ↳ `to_PurchaseOrderItem` | json | Created PO items returned in deep insert | +| `data` | json | Created A_PurchaseOrder entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `PurchaseOrder` | string | Auto-assigned purchase order number | +| ↳ `PurchaseOrderType` | string | PO document type | +| ↳ `CompanyCode` | string | Company code | +| ↳ `PurchasingOrganization` | string | Purchasing organization | +| ↳ `PurchasingGroup` | string | Purchasing group | +| ↳ `Supplier` | string | Supplier business partner key | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `CreationDate` | string | Creation date \(OData /Date\(ms\)/\) | +| ↳ `to_PurchaseOrderItem` | json | Created PO items returned in deep insert | @@ -1793,16 +1743,14 @@ Update fields on an A_PurchaseOrder header in SAP S/4HANA Cloud (API_PURCHASEORD | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP \(204 on success\) | -| `data` | json | Null on 204 success, or OData v2 envelope with updated A_PurchaseOrder at output.data.d | -| ↳ `d` | json | Updated A_PurchaseOrder entity \(if returned\) | -| ↳ `PurchaseOrder` | string | Purchase order number | -| ↳ `PurchaseOrderType` | string | PO document type | -| ↳ `CompanyCode` | string | Company code | -| ↳ `PurchasingGroup` | string | Purchasing group | -| ↳ `Supplier` | string | Supplier key | -| ↳ `NetAmount` | string | Net amount | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `LastChangeDateTime` | string | Last change timestamp | +| `data` | json | Null on 204 No Content \(the usual response\), or the updated A_PurchaseOrder entity when SAP returns one | +| ↳ `PurchaseOrder` | string | Purchase order number | +| ↳ `PurchaseOrderType` | string | PO document type | +| ↳ `CompanyCode` | string | Company code | +| ↳ `PurchasingGroup` | string | Purchasing group | +| ↳ `Supplier` | string | Supplier key | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `LastChangeDateTime` | string | Last change timestamp | @@ -1838,26 +1786,23 @@ List supplier invoices from SAP S/4HANA Cloud (API_SUPPLIERINVOICE_PROCESS_SRV, | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; collection at output.data.d.results | -| ↳ `d` | json | OData v2 envelope | -| ↳ `results` | array | A_SupplierInvoice entities | -| ↳ `SupplierInvoice` | string | Supplier invoice number | -| ↳ `FiscalYear` | string | Fiscal year | -| ↳ `CompanyCode` | string | Company code | -| ↳ `DocumentDate` | string | Invoice document date | -| ↳ `PostingDate` | string | Posting date | -| ↳ `InvoicingParty` | string | Invoicing party \(supplier key\) | -| ↳ `InvoiceGrossAmount` | string | Gross invoice amount | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `AccountingDocumentType` | string | Accounting document type | -| ↳ `PaymentTerms` | string | Payment terms key | -| ↳ `DueCalculationBaseDate` | string | Baseline date for due-date calculation | -| ↳ `SupplierInvoiceIDByInvcgParty` | string | Reference number used by the invoicing party | -| ↳ `PaymentMethod` | string | Payment method | -| ↳ `TaxIsCalculatedAutomatically` | boolean | Whether tax is calculated automatically | -| ↳ `ManualCashDiscount` | string | Manually entered cash discount amount | -| ↳ `BusinessPlace` | string | Business place \(jurisdiction code\) | -| ↳ `__next` | string | OData skiptoken URL for next page | +| `data` | json | Array of A_SupplierInvoice entities, or `\{ results, __count?, __next? \}` when pagination metadata is present \(the integration unwraps the OData v2 `d` envelope\). Properties below describe each item. | +| ↳ `SupplierInvoice` | string | Supplier invoice number | +| ↳ `FiscalYear` | string | Fiscal year | +| ↳ `CompanyCode` | string | Company code | +| ↳ `DocumentDate` | string | Invoice document date | +| ↳ `PostingDate` | string | Posting date | +| ↳ `InvoicingParty` | string | Invoicing party \(supplier key\) | +| ↳ `InvoiceGrossAmount` | string | Gross invoice amount | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `AccountingDocumentType` | string | Accounting document type | +| ↳ `PaymentTerms` | string | Payment terms key | +| ↳ `DueCalculationBaseDate` | string | Baseline date for due-date calculation | +| ↳ `SupplierInvoiceIDByInvcgParty` | string | Reference number used by the invoicing party | +| ↳ `PaymentMethod` | string | Payment method | +| ↳ `TaxIsCalculatedAutomatically` | boolean | Whether tax is calculated automatically | +| ↳ `ManualCashDiscount` | string | Manually entered cash discount amount | +| ↳ `BusinessPlace` | string | Business place \(jurisdiction code\) | @@ -1891,24 +1836,23 @@ Retrieve a single supplier invoice by composite key (SupplierInvoice + FiscalYea | Parameter | Type | Description | | --------- | ---- | ----------- | | `status` | number | HTTP status code returned by SAP | -| `data` | json | OData v2 response envelope; entity at output.data.d | -| ↳ `d` | json | A_SupplierInvoice entity | -| ↳ `SupplierInvoice` | string | Supplier invoice number | -| ↳ `FiscalYear` | string | Fiscal year | -| ↳ `CompanyCode` | string | Company code | -| ↳ `DocumentDate` | string | Invoice document date | -| ↳ `PostingDate` | string | Posting date | -| ↳ `InvoicingParty` | string | Invoicing party \(supplier key\) | -| ↳ `InvoiceGrossAmount` | string | Gross invoice amount | -| ↳ `DocumentCurrency` | string | Document currency | -| ↳ `AccountingDocumentType` | string | Accounting document type | -| ↳ `PaymentTerms` | string | Payment terms key | -| ↳ `DueCalculationBaseDate` | string | Baseline date for due-date calculation | -| ↳ `SupplierInvoiceIDByInvcgParty` | string | Reference number used by the invoicing party | -| ↳ `PaymentMethod` | string | Payment method | -| ↳ `TaxIsCalculatedAutomatically` | boolean | Whether tax is calculated automatically | -| ↳ `ManualCashDiscount` | string | Manually entered cash discount amount | -| ↳ `BusinessPlace` | string | Business place \(jurisdiction code\) | +| `data` | json | A_SupplierInvoice entity \(the integration unwraps the OData v2 `d` envelope\) | +| ↳ `SupplierInvoice` | string | Supplier invoice number | +| ↳ `FiscalYear` | string | Fiscal year | +| ↳ `CompanyCode` | string | Company code | +| ↳ `DocumentDate` | string | Invoice document date | +| ↳ `PostingDate` | string | Posting date | +| ↳ `InvoicingParty` | string | Invoicing party \(supplier key\) | +| ↳ `InvoiceGrossAmount` | string | Gross invoice amount | +| ↳ `DocumentCurrency` | string | Document currency | +| ↳ `AccountingDocumentType` | string | Accounting document type | +| ↳ `PaymentTerms` | string | Payment terms key | +| ↳ `DueCalculationBaseDate` | string | Baseline date for due-date calculation | +| ↳ `SupplierInvoiceIDByInvcgParty` | string | Reference number used by the invoicing party | +| ↳ `PaymentMethod` | string | Payment method | +| ↳ `TaxIsCalculatedAutomatically` | boolean | Whether tax is calculated automatically | +| ↳ `ManualCashDiscount` | string | Manually entered cash discount amount | +| ↳ `BusinessPlace` | string | Business place \(jurisdiction code\) | diff --git a/apps/docs/content/docs/integrations/square.mdx b/apps/docs/content/docs/integrations/square.mdx index 9b18a7dc50b..4a2ec05548a 100644 --- a/apps/docs/content/docs/integrations/square.mdx +++ b/apps/docs/content/docs/integrations/square.mdx @@ -1375,6 +1375,9 @@ Create or update a catalog object such as an item, variation, or category | ↳ `item_variation_data` | json | Variation-specific data \(when type is ITEM_VARIATION\) | | ↳ `category_data` | json | Category-specific data \(when type is CATEGORY\) | | ↳ `image_data` | json | Image-specific data \(when type is IMAGE\) | +| `id_mappings` | array | Mapping from each temporary #-prefixed client ID to its permanent Square ID | +| ↳ `client_object_id` | string | Client-supplied temporary #-prefixed ID | +| ↳ `object_id` | string | Permanent ID Square assigned to the object | | `metadata` | json | Catalog object summary metadata | | ↳ `id` | string | Square catalog object ID | | ↳ `type` | string | Catalog object type | @@ -1410,6 +1413,17 @@ Retrieve a single catalog object by its ID | ↳ `item_variation_data` | json | Variation-specific data \(when type is ITEM_VARIATION\) | | ↳ `category_data` | json | Category-specific data \(when type is CATEGORY\) | | ↳ `image_data` | json | Image-specific data \(when type is IMAGE\) | +| `related_objects` | array | Catalog objects referenced by the retrieved object \(returned when Include Related Objects is true\) | +| ↳ `type` | string | Type of catalog object \(ITEM, ITEM_VARIATION, CATEGORY, IMAGE, etc.\) | +| ↳ `id` | string | Unique ID for the catalog object | +| ↳ `version` | number | Optimistic concurrency version of the object | +| ↳ `updated_at` | string | Timestamp when the object was last updated \(RFC 3339\) | +| ↳ `is_deleted` | boolean | Whether the object is deleted | +| ↳ `present_at_all_locations` | boolean | Whether the object is present at all locations | +| ↳ `item_data` | json | Item-specific data \(when type is ITEM\) | +| ↳ `item_variation_data` | json | Variation-specific data \(when type is ITEM_VARIATION\) | +| ↳ `category_data` | json | Category-specific data \(when type is CATEGORY\) | +| ↳ `image_data` | json | Image-specific data \(when type is IMAGE\) | | `metadata` | json | Catalog object summary metadata | | ↳ `id` | string | Square catalog object ID | | ↳ `type` | string | Catalog object type | diff --git a/apps/docs/content/docs/integrations/supabase.mdx b/apps/docs/content/docs/integrations/supabase.mdx index 1c1f8effb13..c030a2db0b4 100644 --- a/apps/docs/content/docs/integrations/supabase.mdx +++ b/apps/docs/content/docs/integrations/supabase.mdx @@ -33,7 +33,7 @@ Query data from a Supabase table | `schema` | string | No | Database schema to query from \(default: public\). Use this to access tables in other schemas. | | `select` | string | No | Columns to return \(comma-separated\). Defaults to * \(all columns\) | | `filter` | string | No | PostgREST filter \(e.g., "id=eq.123"\) | -| `orderBy` | string | No | Column to order by \(add DESC for descending\) | +| `orderBy` | string | No | Column\(s\) to order by, comma-separated \(e.g., "created_at DESC" or "created_at.desc,name.asc"\) | | `limit` | number | No | Maximum number of rows to return | | `offset` | number | No | Number of rows to skip \(for pagination\) | @@ -288,7 +288,7 @@ Invoke a Supabase Edge Function over HTTP ### Supabase Introspect -Introspect Supabase database schema from its OpenAPI spec to get table and column structures (best-effort primary/foreign key detection) +Introspect Supabase database schema from its OpenAPI spec to get table and column structures, primary keys, and foreign keys @@ -311,9 +311,9 @@ Introspect Supabase database schema from its OpenAPI spec to get table and colum | ↳ `type` | string | Column data type | | ↳ `nullable` | boolean | Whether the column allows null values — a NOT NULL column that has a default value is misreported as nullable, since the OpenAPI spec this is derived from omits it from the required list in that case | | ↳ `default` | string | Default value for the column | -| ↳ `isPrimaryKey` | boolean | Best-effort guess based on the column being named "id" \(not authoritative\) | -| ↳ `isForeignKey` | boolean | True only if the column has a "references table.column" SQL comment; most databases will show false even for real foreign keys | -| ↳ `references` | object | Foreign key reference details, when detected via SQL comment | +| ↳ `isPrimaryKey` | boolean | Whether the column is part of the table primary key | +| ↳ `isForeignKey` | boolean | Whether the column is a foreign key to another table | +| ↳ `references` | object | Foreign key reference details | | ↳ `table` | string | Referenced table name | | ↳ `column` | string | Referenced column name | | ↳ `primaryKey` | array | Array of primary key column names | @@ -473,8 +473,8 @@ Move a file within a Supabase storage bucket | `message` | string | Operation status message | | `results` | object | Move operation result | | ↳ `message` | string | Operation status message | -| ↳ `Id` | string | Identifier of the destination object | -| ↳ `Key` | string | Full object key of the destination | +| ↳ `Id` | string | Not returned by the move endpoint, which responds with only a message | +| ↳ `Key` | string | Not returned by the move endpoint, which responds with only a message | diff --git a/apps/docs/content/docs/integrations/workday.mdx b/apps/docs/content/docs/integrations/workday.mdx index 7a6012fe8f0..3274beaa348 100644 --- a/apps/docs/content/docs/integrations/workday.mdx +++ b/apps/docs/content/docs/integrations/workday.mdx @@ -35,7 +35,7 @@ Retrieve a specific worker profile including personal, employment, and organizat | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID to retrieve \(e.g., 3aa5550b7fe348b98d7b5741afc65534\) | +| `workerId` | string | Yes | Employee ID of the worker to retrieve \(e.g., 21001\) | #### Output @@ -59,7 +59,7 @@ List or search workers with optional filtering and pagination. | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `limit` | number | No | Maximum number of workers to return \(default: 20\) | +| `limit` | number | No | Maximum number of workers to return, 1-999 \(default: 20\) | | `offset` | number | No | Number of records to skip for pagination | #### Output @@ -88,7 +88,7 @@ Create a new pre-hire (applicant) record in Workday. This is typically the first | `legalName` | string | Yes | Full legal name of the pre-hire \(e.g., "Jane Doe"\) | | `email` | string | No | Email address of the pre-hire | | `phoneNumber` | string | No | Phone number of the pre-hire | -| `address` | string | No | Address of the pre-hire | +| `address` | string | No | Street address of the pre-hire, sent as address line 1. City, region, and postal code are not sent, so tenants whose country address rules require them will reject it | | `countryCode` | string | No | ISO 3166-1 Alpha-2 country code \(defaults to US\) | #### Output @@ -144,8 +144,8 @@ Update fields on an existing worker record in Workday. | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID to update | -| `fields` | json | Yes | Fields to update as JSON \(e.g., \{"businessTitle": "Senior Engineer", "primaryWorkEmail": "new@company.com"\}\) | +| `workerId` | string | Yes | Employee ID of the worker to update | +| `fields` | json | Yes | Personal information fields to update as JSON, using Change_Personal_Information_Data element names in schema order \(e.g., \{"Marital_Status_Reference": \{"ID": \{"attributes": \{"wd:type": "Marital_Status_ID"\}, "$value": "Married"\}\}\}\) | #### Output @@ -170,7 +170,7 @@ Create or update an onboarding plan assignment for a worker. Sets up onboarding | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID to assign the onboarding plan to | +| `workerId` | string | Yes | Workday ID \(WID\) of the person to assign the onboarding plan to | | `onboardingPlanId` | string | Yes | Onboarding plan ID to assign | | `actionEventId` | string | Yes | Action event ID that enables the onboarding plan \(e.g., the hiring event ID\) | @@ -199,7 +199,7 @@ Retrieve organizations, departments, and cost centers from Workday. | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | | `type` | string | No | Organization type filter \(e.g., Supervisory, Cost_Center, Company, Region\) | -| `limit` | number | No | Maximum number of organizations to return \(default: 20\) | +| `limit` | number | No | Maximum number of organizations to return, 1-999 \(default: 20\) | | `offset` | number | No | Number of records to skip for pagination | #### Output @@ -225,7 +225,7 @@ Perform a job change for a worker including transfers, promotions, demotions, an | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID for the job change | +| `workerId` | string | Yes | Employee ID of the worker for the job change | | `effectiveDate` | string | Yes | Effective date for the job change in ISO 8601 format \(e.g., 2025-06-01\) | | `newPositionId` | string | No | New position ID \(for transfers\) | | `newJobProfileId` | string | No | New job profile ID \(for role changes\) | @@ -257,7 +257,7 @@ Retrieve compensation plan details for a specific worker. | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID to retrieve compensation data for | +| `workerId` | string | Yes | Employee ID of the worker to retrieve compensation data for | #### Output @@ -286,7 +286,7 @@ Initiate a worker termination in Workday. Triggers the Terminate Employee busine | `tenant` | string | Yes | Workday tenant name | | `username` | string | Yes | Integration System User username | | `password` | string | Yes | Integration System User password | -| `workerId` | string | Yes | Worker ID to terminate | +| `workerId` | string | Yes | Employee ID of the worker to terminate | | `terminationDate` | string | Yes | Termination date in ISO 8601 format \(e.g., 2025-06-01\) | | `reason` | string | Yes | Termination reason \(e.g., Resignation, End_of_Contract, Retirement\) | | `notificationDate` | string | No | Date the termination was communicated in ISO 8601 format | diff --git a/apps/docs/content/docs/integrations/zendesk.mdx b/apps/docs/content/docs/integrations/zendesk.mdx index 883b298cc3e..b3137af306d 100644 --- a/apps/docs/content/docs/integrations/zendesk.mdx +++ b/apps/docs/content/docs/integrations/zendesk.mdx @@ -317,6 +317,7 @@ Update an existing ticket in Zendesk with support for custom fields | `ticketId` | string | Yes | Ticket ID to update as a numeric string \(e.g., "12345"\) | | `subject` | string | No | New ticket subject text | | `comment` | string | No | Comment text to add to the ticket | +| `commentPublic` | boolean | No | Whether the comment is a public reply \(true\) or an internal note \(false\). Omit to keep the ticket default | | `priority` | string | No | Priority: "low", "normal", "high", or "urgent" | | `status` | string | No | Status: "new", "open", "pending", "hold", "solved", or "closed" | | `type` | string | No | Type: "problem", "incident", "question", or "task" | @@ -401,7 +402,7 @@ Update multiple tickets in Zendesk at once (max 100) | `priority` | string | No | New priority for all tickets: "low", "normal", "high", or "urgent" | | `assigneeId` | string | No | New assignee ID for all tickets as a numeric string \(e.g., "12345"\) | | `groupId` | string | No | New group ID for all tickets as a numeric string \(e.g., "67890"\) | -| `tags` | string | No | Comma-separated tags to add to all tickets \(e.g., "bulk-update, processed"\) | +| `tags` | string | No | Comma-separated tags that replace the existing tags on all tickets \(e.g., "bulk-update, processed"\) | #### Output @@ -459,7 +460,7 @@ Merge multiple tickets into a target ticket | --------- | ---- | -------- | ----------- | | `targetTicketId` | string | Yes | Target ticket ID as a numeric string \(tickets will be merged into this one, e.g., "12345"\) | | `sourceTicketIds` | string | Yes | Comma-separated source ticket IDs to merge \(e.g., "111, 222, 333"\) | -| `targetComment` | string | No | Comment text to add to target ticket after merge | +| `targetComment` | string | No | Comment text to add to the target ticket after merge, posted as a public reply | #### Output @@ -1247,7 +1248,7 @@ Unified search across tickets, users, and organizations in Zendesk | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `query` | string | Yes | Search query string using Zendesk search syntax \(e.g., "type:ticket status:open"\) | +| `query` | string | Yes | Search query string using Zendesk search syntax \(e.g., "status:open priority:urgent"\). The resource type comes from filterType; a "type:" keyword in the query is dropped | | `filterType` | string | Yes | Resource type to search for: "ticket", "user", "organization", or "group" | | `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) | | `pageAfter` | string | No | Cursor from a previous response to fetch the next page of results | diff --git a/apps/sim/app/api/v2/lib/response.ts b/apps/sim/app/api/v2/lib/response.ts index 82459661bec..1e95d321f71 100644 --- a/apps/sim/app/api/v2/lib/response.ts +++ b/apps/sim/app/api/v2/lib/response.ts @@ -22,6 +22,7 @@ import { type OrchestrationErrorCode, } from '@/lib/core/orchestration/types' import type { HttpError } from '@/lib/core/utils/http-error' +import { describeResponseError } from '@/lib/core/utils/response-error' import type { RateLimitResult } from '@/app/api/v1/middleware' /** @@ -217,7 +218,7 @@ export function v2Error( const retryAfterSeconds = options.omitRetryAfter ? undefined : RETRY_AFTER_SECONDS_BY_STATUS[status] - return NextResponse.json( + const response = NextResponse.json( { error }, { status, @@ -229,6 +230,15 @@ export function v2Error( }, } ) + const reason = detailCode(options.details) + describeResponseError(response, { errorCode: code, ...(reason ? { errorReason: reason } : {}) }) + return response +} + +/** The cause a refusal names in `details.code`, a closed vocabulary safe to log. */ +function detailCode(details: unknown): string | undefined { + if (typeof details !== 'object' || details === null || !('code' in details)) return undefined + return typeof details.code === 'string' ? details.code : undefined } /** Renders a trusted typed HTTP error without changing the v2 envelope. */ @@ -267,9 +277,17 @@ export function v2ValidationError(error: ZodError, slice?: RequestSlice): NextRe ? issueMessage.slice(fieldLabel.length) : issueMessage const location = [slice, ...path].filter((part) => part !== undefined && part !== '').join('.') - return v2Error('BAD_REQUEST', location ? `${location}: ${message}` : message, { + const response = v2Error('BAD_REQUEST', location ? `${location}: ${message}` : message, { details: serializeZodIssues(error), }) + // Deeper segments can be record keys the caller wrote (an import's CSV headers); no slice is itself a record. + const field = [slice, path[0]].filter((part) => part !== undefined && part !== '').join('.') + describeResponseError(response, { + errorCode: 'BAD_REQUEST', + ...(field ? { errorField: field } : {}), + ...(error.issues[0] ? { errorReason: error.issues[0].code } : {}), + }) + return response } /** diff --git a/apps/sim/app/extension/layout.tsx b/apps/sim/app/extension/layout.tsx index a8cff1d9d89..083ff5fd7e7 100644 --- a/apps/sim/app/extension/layout.tsx +++ b/apps/sim/app/extension/layout.tsx @@ -13,15 +13,13 @@ interface ExtensionLayoutProps { export const metadata = { title: 'Sim', robots: { index: false, follow: false } } +/** + * The compact chat surface. The browser extension frames it in its side panel, and the desktop + * app's quick chat window opens it top-level, so it renders on deployments without the extension + * too: with no extension origins the proxy's `frame-ancestors 'none'` keeps it unframeable. + */ export default async function ExtensionLayout({ children }: ExtensionLayoutProps) { const origins = getBrowserExtensionOrigins() - if (origins.length === 0) { - return ( -
- The browser extension is not enabled for this Sim deployment. -
- ) - } const session = await getSession() if (!session?.user) return ( diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/watch-activity/watch-activity.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/watch-activity/watch-activity.tsx index dc833b8df6e..540932f8500 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/watch-activity/watch-activity.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/watch-activity/watch-activity.tsx @@ -8,7 +8,8 @@ interface WatchActivityProps { task: TaskBlockInfo } -function watchLabel(task: TaskBlockInfo, routedWorkspaceId?: string): string { +/** A task's one-line state, shared by its watch row and the turn its notification opens. */ +export function watchLabel(task: TaskBlockInfo, routedWorkspaceId?: string): string { const status = task.status ?? 'pending' if (task.kind === 'workflow_run') { const workspaceId = diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx index 2eca1fc5feb..bd01970982c 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx @@ -20,13 +20,16 @@ import { scrollFadeClass, useScrollEdges, } from '@sim/emcn' +import { Calendar, Clock } from '@sim/emcn/icons' import { useQueryClient } from '@tanstack/react-query' import { defaultRangeExtractor, type Range, useVirtualizer } from '@tanstack/react-virtual' +import { useParams } from 'next/navigation' import { AssistantActionReviewMessage, useHasAssistantActionReview, } from '@/components/assistant-actions' import { SMOOTH_CHASE_RATE } from '@/lib/core/utils/smooth-bottom-chase' +import { parseAutomatedTurn } from '@/lib/mothership/chat/automated-turn' import type { TurnOrigin } from '@/lib/mothership/generated/protocol' import { inter } from '@/app/_styles/fonts/inter/inter' import { FindBar } from '@/app/workspace/[workspaceId]/components/find-bar' @@ -49,6 +52,7 @@ import { parseLastQuestionTag, } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags' import type { SearchIntegrationConnectionProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/search-integration-connection' +import { watchLabel } from '@/app/workspace/[workspaceId]/home/components/message-content/components/watch-activity/watch-activity' import { prepareCopyableMarkdown, toCopyableMarkdown, @@ -62,6 +66,7 @@ import { } from '@/app/workspace/[workspaceId]/home/components/user-input' import type { ModelSelectorOptions } from '@/app/workspace/[workspaceId]/home/components/user-input/components/model-selector' import { UserMessageContent } from '@/app/workspace/[workspaceId]/home/components/user-message-content' +import { useResourceNameRevision } from '@/app/workspace/[workspaceId]/home/hooks/use-tool-resource-titles' import { fetchWorkspaceResourceRef, readCachedWorkspaceResourceRef, @@ -204,37 +209,55 @@ interface UserMessageRowProps { attachmentWidthClassName: string } +function describeAutomatedTurn( + origin: TurnOrigin, + content: string, + workspaceId?: string +): { label: string; text: string; note?: string } { + const turn = parseAutomatedTurn(origin, content) + if (turn?.origin === 'task') { + return { + label: 'Background task', + text: watchLabel(turn.task, workspaceId), + note: turn.task.note || undefined, + } + } + if (turn?.origin === 'job') return { label: `Scheduled job · ${turn.run}`, text: turn.title } + return origin === 'task' + ? { label: 'Background task', text: 'Task finished' } + : { label: 'Scheduled job', text: 'Run started' } +} + /** - * A turn automation opened, never a user bubble — the user did not type it: a muted system chip - * with one line. A background-task notification (mothership 21-background-tasks.md §6.4) shows - * its outcome line; a scheduled job's run shows the brief's first line (the job and its run). + * A turn automation opened (mothership 21-background-tasks.md §6.4): a message in the user's + * lane, labeled with what sent it. A background task shows the same line as its watch row; a + * scheduled job shows its title and run. */ interface AutomatedTurnRowProps { origin: TurnOrigin content: string rowClassName: string + bubbleClassName: string } const AutomatedTurnRow = memo(function AutomatedTurnRow({ origin, content, rowClassName, + bubbleClassName, }: AutomatedTurnRowProps) { - const lines = content.split('\n').filter((line) => line.length > 0) - const summary = - origin === 'task' - ? (lines.find((line) => line.startsWith('Task ')) ?? lines[lines.length - 1] ?? '').replace( - /^Task [0-9a-f-]+ /, - '' - ) - : (lines[0] ?? '').replace(/^Job: /, '') + const params = useParams<{ workspaceId?: string }>() + useResourceNameRevision() + const { label, text, note } = describeAutomatedTurn(origin, content, params?.workspaceId) + const Icon = origin === 'task' ? Clock : Calendar return ( -
-
- - {origin === 'task' ? 'Background task' : 'Scheduled job'} - - {summary} +
+
+ + {label} +
+
+
) @@ -995,7 +1018,8 @@ export function MothershipChat({ ) : ( value + 1, 0) + useEffect( + () => + getQueryClient() + .getQueryCache() + .subscribe((event) => { + if ( + event.type === 'updated' && + event.action.type === 'success' && + isResourceNameQuery(event.query.queryKey) + ) + refresh() + }), + [] + ) + return revision +} + /** Refresh existing rows when the sidebar inventory arrives or a resource is renamed. */ export function useToolResourceTitles(blocks: ContentBlock[]): ContentBlock[] { const overlayVersion = useCustomBlockOverlayVersion() const params = useParams<{ workspaceId?: string }>() const workspaceId = params?.workspaceId - const [revision, refresh] = useReducer((value: number) => value + 1, 0) - useEffect(() => { - const cache = getQueryClient().getQueryCache() - return cache.subscribe((event) => { - if ( - event.type === 'updated' && - event.action.type === 'success' && - isResourceNameQuery(event.query.queryKey) - ) - refresh() - }) - }, [workspaceId]) + const revision = useResourceNameRevision() return useMemo( () => diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/desktop/desktop.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/desktop/desktop.tsx index b96eae87fb4..a1ba37a3061 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/desktop/desktop.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/desktop/desktop.tsx @@ -1,11 +1,16 @@ 'use client' import { useEffect, useState } from 'react' -import type { DesktopPreferenceKey, DesktopPreferences } from '@sim/desktop-bridge' +import type { + DesktopPreferenceKey, + DesktopPreferences, + QuickChatShortcut, +} from '@sim/desktop-bridge' import { Label, Switch, toast } from '@sim/emcn' import { useParams, useRouter } from 'next/navigation' import { getDesktopBridge, getDesktopShellVersion } from '@/lib/desktop' import { ComputerUseSettings } from '@/app/workspace/[workspaceId]/settings/components/desktop/computer-use' +import { QuickChatShortcutSelect } from '@/app/workspace/[workspaceId]/settings/components/desktop/quick-chat-shortcut-select' import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel' import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' import { useDesktopPreferenceMutation } from '@/hooks/use-desktop-preference-mutation' @@ -82,6 +87,14 @@ export function Desktop() { setPreferences ) + const { pending: quickChatShortcutPending, mutate: setQuickChatShortcut } = + useDesktopPreferenceMutation( + async (bridge, shortcut: QuickChatShortcut | null) => + bridge.settings.setQuickChatShortcut?.(shortcut), + 'Could not update the quick chat shortcut', + setPreferences + ) + if (!preferences) { return null } @@ -131,6 +144,24 @@ export function Desktop() { disabled={pendingPreference !== null} onCheckedChange={(checked) => void updatePreference('trayEnabled', checked)} /> + {preferences.quickChatShortcut !== undefined && ( +
+
+ + {preferences.quickChatShortcut && + preferences.quickChatShortcutAvailable === false && ( +

+ Another app is using this shortcut +

+ )} +
+ void setQuickChatShortcut(shortcut)} + /> +
+ )} = { + 'Alt+Space': '⌥ Space', + 'CommandOrControl+Shift+Space': '⇧⌘ Space', + 'CommandOrControl+Shift+J': '⇧⌘J', +} + +const OFF = 'off' + +interface QuickChatShortcutSelectProps { + value: QuickChatShortcut | null + onChange: (value: QuickChatShortcut | null) => void + disabled?: boolean +} + +/** Picks the global shortcut for the desktop quick chat window, or turns it off. */ +export function QuickChatShortcutSelect({ + value, + onChange, + disabled, +}: QuickChatShortcutSelectProps) { + const mac = isMacPlatform() + return ( +
+ onChange(isQuickChatShortcut(next) ? next : null)} + disabled={disabled} + options={[ + { label: 'Off', value: OFF }, + ...QUICK_CHAT_SHORTCUTS.map((shortcut) => ({ + label: mac ? MAC_LABELS[shortcut] : shortcut.replace('CommandOrControl', 'Ctrl'), + value: shortcut, + })), + ]} + /> +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 9c7b014571a..0d6606e191f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -1190,7 +1190,7 @@ function SubBlockComponent({ subBlockId={config.id} content={ typeof config.value === 'function' - ? config.value(subBlockValues || {}) + ? (config.value(subBlockValues || {}) ?? '') : (config.defaultValue as string) || '' } /> diff --git a/apps/sim/blocks/blocks/airtable.test.ts b/apps/sim/blocks/blocks/airtable.test.ts index 4d884c9c8d5..d5591f2436a 100644 --- a/apps/sim/blocks/blocks/airtable.test.ts +++ b/apps/sim/blocks/blocks/airtable.test.ts @@ -91,4 +91,37 @@ describe('AirtableBlock typecast', () => { }) ).not.toHaveProperty('typecast') }) + + it.each(operationCases)( + 'accepts already-parsed $operation values from a tool call or a second mapping pass', + ({ operation, payload }) => { + expect(buildParams({ ...baseParams, operation, ...payload })).toMatchObject(payload) + } + ) + + it('accepts already-parsed record IDs for delete', () => { + expect( + buildParams({ ...baseParams, operation: 'delete', recordIds: ['rec123'] }) + ).toMatchObject({ recordIds: ['rec123'] }) + }) +}) + +describe('Airtable pagination offsets', () => { + const mapParams = AirtableBlock.tools.config?.params + if (!mapParams) throw new Error('Airtable block is missing its params mapper') + + it('does not send a List Bases offset to List Records on the canvas', () => { + const mapped = mapParams({ ...baseParams, operation: 'list', basesOffset: 'itrBases' }) + expect(mapped.offset).toBeUndefined() + }) + + it('keeps the model offset on a List Records tool call', () => { + const mapped = mapParams({ ...baseParams, offset: 'itrModel' }, { operation: 'list' }) + expect(mapped.offset).toBe('itrModel') + }) + + it('maps an offset the user set on the tool when the operation arrives only on the context', () => { + const mapped = mapParams({ ...baseParams, recordsOffset: 'itrUser' }, { operation: 'list' }) + expect(mapped.offset).toBe('itrUser') + }) }) diff --git a/apps/sim/blocks/blocks/airtable.ts b/apps/sim/blocks/blocks/airtable.ts index 59f0b98dc1f..139dafaa2ad 100644 --- a/apps/sim/blocks/blocks/airtable.ts +++ b/apps/sim/blocks/blocks/airtable.ts @@ -1,7 +1,9 @@ +import { getErrorMessage } from '@sim/utils/errors' import { AirtableIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { toolCallArg } from '@/blocks/utils' import { getTrigger } from '@/triggers' /** Canonical account pair — an advanced-mode card only fills `manualCredential`. */ @@ -169,6 +171,38 @@ export const AirtableBlock: BlockConfig = { condition: { field: 'operation', value: 'list' }, mode: 'advanced', }, + { + id: 'view', + title: 'View', + type: 'short-input', + placeholder: 'View name or ID (e.g., viwXXXXXXXXXXXXXX)', + condition: { field: 'operation', value: 'list' }, + mode: 'advanced', + }, + { + id: 'pageSize', + title: 'Page Size', + type: 'short-input', + placeholder: 'Records per page (max 100, default 100)', + condition: { field: 'operation', value: 'list' }, + mode: 'advanced', + }, + { + id: 'recordsOffset', + title: 'Offset', + type: 'short-input', + placeholder: 'Pagination offset from a previous response', + condition: { field: 'operation', value: 'list' }, + mode: 'advanced', + }, + { + id: 'basesOffset', + title: 'Offset', + type: 'short-input', + placeholder: 'Pagination offset from a previous response', + condition: { field: 'operation', value: 'listBases' }, + mode: 'advanced', + }, { id: 'filterFormula', title: 'Filter Formula', @@ -382,35 +416,49 @@ Return ONLY the valid JSON array of record ID strings - no explanations, no mark throw new Error(`Invalid Airtable operation: ${params.operation}`) } }, - params: (params) => { - const { oauthCredential, records, fields, fieldsToMergeOn, recordIds, typecast, ...rest } = - params - let parsedRecords: any | undefined - let parsedFields: any | undefined - let parsedFieldsToMergeOn: any | undefined - let parsedRecordIds: any | undefined + params: (params, context) => { + const { + oauthCredential, + records, + fields, + fieldsToMergeOn, + recordIds, + typecast, + recordsOffset, + basesOffset, + ...rest + } = params + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const operation = params.operation ?? context?.operation + let parsedRecords: unknown + let parsedFields: unknown + let parsedFieldsToMergeOn: unknown + let parsedRecordIds: unknown + + /** Tool-call args and an already-mapped pass arrive parsed; only canvas code strings need parsing. */ + const parseJsonInput = (value: unknown): unknown => + typeof value === 'string' ? JSON.parse(value) : value - // Parse JSON inputs safely try { if ( records && - (params.operation === 'create' || - params.operation === 'updateMultiple' || - params.operation === 'upsert') + (operation === 'create' || operation === 'updateMultiple' || operation === 'upsert') ) { - parsedRecords = JSON.parse(records) + parsedRecords = parseJsonInput(records) } - if (fields && params.operation === 'update') { - parsedFields = JSON.parse(fields) + if (fields && operation === 'update') { + parsedFields = parseJsonInput(fields) } - if (fieldsToMergeOn && params.operation === 'upsert') { - parsedFieldsToMergeOn = JSON.parse(fieldsToMergeOn) + if (fieldsToMergeOn && operation === 'upsert') { + parsedFieldsToMergeOn = parseJsonInput(fieldsToMergeOn) } - if (recordIds && params.operation === 'delete') { - parsedRecordIds = JSON.parse(recordIds) + if (recordIds && operation === 'delete') { + parsedRecordIds = parseJsonInput(recordIds) } - } catch (error: any) { - throw new Error(`Invalid JSON input for ${params.operation} operation: ${error.message}`) + } catch (error) { + throw new Error( + `Invalid JSON input for ${operation} operation: ${getErrorMessage(error)}` + ) } // Construct parameters based on operation @@ -421,7 +469,17 @@ Return ONLY the valid JSON array of record ID strings - no explanations, no mark const typecastParams = typecast != null ? { typecast: typecast === true || typecast === 'true' } : {} - switch (params.operation) { + switch (operation) { + case 'list': + return { + ...baseParams, + offset: recordsOffset || toolCallArg(context, rest.offset) || undefined, + } + case 'listBases': + return { + ...baseParams, + offset: basesOffset || toolCallArg(context, rest.offset) || undefined, + } case 'create': case 'updateMultiple': return { ...baseParams, records: parsedRecords, ...typecastParams } @@ -451,6 +509,16 @@ Return ONLY the valid JSON array of record ID strings - no explanations, no mark recordId: { type: 'string', description: 'Record identifier' }, // Required for get/update maxRecords: { type: 'number', description: 'Maximum records to return' }, // Optional for list filterFormula: { type: 'string', description: 'Filter formula expression' }, // Optional for list + view: { type: 'string', description: 'View name or ID to list records from' }, + pageSize: { type: 'number', description: 'Records per page (max 100)' }, + recordsOffset: { + type: 'string', + description: 'Pagination offset from a previous List Records response', + }, + basesOffset: { + type: 'string', + description: 'Pagination offset from a previous List Bases response', + }, records: { type: 'json', description: 'Record data array' }, // Required for create/updateMultiple/upsert fields: { type: 'json', description: 'Field data object' }, // Required for update single fieldsToMergeOn: { type: 'json', description: 'Field names to match records on' }, // Required for upsert diff --git a/apps/sim/blocks/blocks/amplitude.ts b/apps/sim/blocks/blocks/amplitude.ts index 0adc3b1ce57..22ff60e1c80 100644 --- a/apps/sim/blocks/blocks/amplitude.ts +++ b/apps/sim/blocks/blocks/amplitude.ts @@ -574,7 +574,7 @@ export const AmplitudeBlock: BlockConfig = { id: 'segmentationLimit', title: 'Limit', type: 'short-input', - placeholder: 'Max group-by values (max 1000)', + placeholder: 'Max group-by values (default 1000, max 20000)', condition: { field: 'operation', value: 'event_segmentation' }, mode: 'advanced', }, @@ -610,6 +610,14 @@ export const AmplitudeBlock: BlockConfig = { }, mode: 'advanced', }, + { + id: 'segmentationSecondEventType', + title: 'Second Event Type', + type: 'short-input', + placeholder: 'Event B for a formula, e.g. UNIQUES(A)/UNIQUES(B)', + condition: { field: 'operation', value: 'event_segmentation' }, + mode: 'advanced', + }, { id: 'segmentationSegment', title: 'Segment Definition', @@ -932,6 +940,7 @@ export const AmplitudeBlock: BlockConfig = { { label: 'N-Day', id: 'n-day' }, { label: 'Rolling', id: 'rolling' }, { label: 'Bracket', id: 'bracket' }, + { label: 'N-Day or Before', id: 'nday_or_before' }, ], value: () => 'n-day', condition: { field: 'operation', value: 'retention' }, @@ -941,7 +950,7 @@ export const AmplitudeBlock: BlockConfig = { id: 'retentionBrackets', title: 'Retention Brackets', type: 'short-input', - placeholder: '[[0,4]] — required when Retention Mode is Bracket', + placeholder: '[[0,5]] (end day exclusive) — required when Retention Mode is Bracket', condition: { field: 'operation', value: 'retention', @@ -1033,6 +1042,8 @@ export const AmplitudeBlock: BlockConfig = { case 'event_segmentation': if (params.segmentationEventType) result.eventType = params.segmentationEventType + if (params.segmentationSecondEventType) + result.secondEventType = params.segmentationSecondEventType if (params.segmentationStart) result.start = params.segmentationStart if (params.segmentationEnd) result.end = params.segmentationEnd if (params.segmentationMetric) result.metric = params.segmentationMetric @@ -1122,6 +1133,10 @@ export const AmplitudeBlock: BlockConfig = { dataResidency: { type: 'string', description: 'Data residency region: "us" or "eu"' }, segmentationFilters: { type: 'string', description: 'Event segmentation filters JSON' }, segmentationFormula: { type: 'string', description: 'Event segmentation formula expression' }, + segmentationSecondEventType: { + type: 'string', + description: 'Event segmentation second event type (e2)', + }, segmentationGroupBy2: { type: 'string', description: 'Event segmentation second group-by property', diff --git a/apps/sim/blocks/blocks/asana.test.ts b/apps/sim/blocks/blocks/asana.test.ts new file mode 100644 index 00000000000..96e9d576961 --- /dev/null +++ b/apps/sim/blocks/blocks/asana.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, it } from 'vitest' +import { AsanaBlock } from '@/blocks/blocks/asana' +import type { BlockToolParamsContext } from '@/blocks/types' + +/** The block's `tools.config.params` mapper; `context` is set on a tool call, absent on the canvas. */ +const buildParams = ( + inputs: Record, + context?: BlockToolParamsContext +): Record => AsanaBlock.tools.config?.params?.(inputs, context) ?? {} + +/** + * On an agent call the model supplies args under the tool's own param names (`workspace`, `text`, + * `taskGid`, array `projects`/`followers`), and the stored operation reaches the mapper only on + * `context` (never in the args). The handler forwards `{ ...inputs, ...mapped }`. + */ +describe('AsanaBlock agent tool calls', () => { + const agentCall = (operation: string, args: Record): Record => { + return { ...args, ...buildParams(args, { operation }) } + } + + it('keeps the model workspace, project, limit, and offset on get_task', () => { + const mapped = agentCall('get_task', { + workspace: '111', + assignee: 'me', + project: '222', + limit: 10, + offset: 'tok', + }) + expect(mapped).toMatchObject({ + workspace: '111', + assignee: 'me', + project: '222', + limit: 10, + offset: 'tok', + }) + }) + + it('keeps the model text and array projects on search_tasks', () => { + const mapped = agentCall('search_tasks', { + workspace: '111', + text: 'bug', + projects: ['1', '2'], + limit: 5, + }) + expect(mapped).toMatchObject({ text: 'bug', projects: ['1', '2'], limit: 5 }) + }) + + it('keeps the model text on add_comment', () => { + expect(agentCall('add_comment', { taskGid: '1', text: 'hello' }).text).toBe('hello') + }) + + it('keeps the model taskGid on create_subtask', () => { + expect(agentCall('create_subtask', { taskGid: '9', name: 'Sub' }).taskGid).toBe('9') + }) + + it('keeps the model workspace on create_project', () => { + expect(agentCall('create_project', { workspace: '111', name: 'P' }).workspace).toBe('111') + }) + + it('accepts array followers on add_followers', () => { + expect(agentCall('add_followers', { taskGid: '1', followers: ['3', '4'] }).followers).toEqual([ + '3', + '4', + ]) + }) +}) + +describe('AsanaBlock canvas inputs', () => { + it('drops a raw workspace left over from another operation under get_task', () => { + const inputs = { operation: 'get_task', workspace: 'stale', getTasks_workspace: '111' } + expect({ ...inputs, ...buildParams(inputs) }.workspace).toBe('111') + }) + + it('maps the canvas pagination and search limit fields', () => { + expect( + buildParams({ operation: 'get_task', getTasks_workspace: '1', getTasks_offset: 'tok' }) + ).toMatchObject({ workspace: '1', offset: 'tok' }) + expect( + buildParams({ operation: 'search_tasks', workspace: '1', searchTasks_limit: '25' }) + ).toMatchObject({ limit: 25 }) + }) +}) + +describe('AsanaBlock agent tool calls with a configured field', () => { + it('maps a workspace the user set on the tool to the get_task workspace param', () => { + const mapped = buildParams( + { getTasks_workspace: 'ws-1', assignee: 'me' }, + { operation: 'get_task' } + ) + expect(mapped.workspace).toBe('ws-1') + }) +}) + +describe('AsanaBlock canvas limits', () => { + it('passes a numeric 0 limit through so validation can reject it', () => { + expect( + buildParams({ operation: 'get_task', workspace: 'ws-1', assignee: 'me', getTasks_limit: 0 }) + ).toMatchObject({ limit: 0 }) + }) + + it('ignores a whitespace-only limit field so the model limit applies on a tool call', () => { + expect( + buildParams( + { workspace: 'ws-1', assignee: 'me', getTasks_limit: ' ', limit: 25 }, + { operation: 'get_task' } + ) + ).toMatchObject({ limit: 25 }) + }) +}) diff --git a/apps/sim/blocks/blocks/asana.ts b/apps/sim/blocks/blocks/asana.ts index f978177fa48..37f4325dbd3 100644 --- a/apps/sim/blocks/blocks/asana.ts +++ b/apps/sim/blocks/blocks/asana.ts @@ -2,6 +2,7 @@ import { AsanaIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { toolCallArg } from '@/blocks/utils' const WORKSPACE_FIELD = ['workspaceSelector', 'workspace'] as const const GET_TASKS_WORKSPACE_FIELD = ['getTasksWorkspaceSelector', 'getTasks_workspace'] as const @@ -219,6 +220,17 @@ export const AsanaBlock: BlockConfig = { value: ['get_task'], }, }, + { + id: 'getTasks_offset', + title: 'Page Offset', + type: 'short-input', + placeholder: 'Offset token from a previous next_page.offset', + mode: 'advanced', + condition: { + field: 'operation', + value: ['get_task'], + }, + }, { id: 'name', title: 'Task Name', @@ -250,7 +262,7 @@ export const AsanaBlock: BlockConfig = { placeholder: 'Enter assignee user GID', condition: { field: 'operation', - value: ['create_task', 'update_task', 'search_tasks'], + value: ['get_task', 'create_task', 'update_task', 'search_tasks'], }, }, { @@ -427,6 +439,17 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n value: ['search_tasks'], }, }, + { + id: 'searchTasks_limit', + title: 'Limit', + type: 'short-input', + placeholder: 'Max tasks to return, 1-100', + mode: 'advanced', + condition: { + field: 'operation', + value: ['search_tasks'], + }, + }, { /** * One boolean, so a switch rather than a single-option checkbox list — and a @@ -495,15 +518,29 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n return 'asana_get_task' } }, - params: (params) => { - const { oauthCredential, operation } = params + params: (params, context) => { + const { oauthCredential } = params + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const operation = params.operation ?? context?.operation - const projectsArray = params.projects - ? params.projects - .split(',') - .map((p: string) => p.trim()) - .filter((p: string) => p.length > 0) - : undefined + const splitGids = (value: unknown): string[] | undefined => { + if (Array.isArray(value)) return value.map(String) + if (typeof value !== 'string' || !value) return undefined + return value + .split(',') + .map((gid) => gid.trim()) + .filter((gid) => gid.length > 0) + } + const toLimit = (value: unknown): number | undefined => + value === undefined || value === null || String(value).trim() === '' + ? undefined + : Number(value) + /** A dedicated limit wins when present, so a reference resolving to 0 still reaches validation. */ + const pickLimit = (dedicated: unknown, toolArg: unknown): unknown => + dedicated === undefined || dedicated === null || String(dedicated).trim() === '' + ? toolCallArg(context, toolArg) + : dedicated + const projectsArray = splitGids(params.projects) // Only send a completion value when the user actually set the toggle; an // untouched field must omit it (not send `false`), so update_task doesn't @@ -520,9 +557,11 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n return { ...baseParams, taskGid: params.taskGid, - workspace: params.getTasks_workspace, - project: params.getTasks_project, - limit: params.getTasks_limit ? Number(params.getTasks_limit) : undefined, + workspace: params.getTasks_workspace || toolCallArg(context, params.workspace), + project: params.getTasks_project || toolCallArg(context, params.project), + assignee: params.assignee, + limit: toLimit(pickLimit(params.getTasks_limit, params.limit)), + offset: params.getTasks_offset || toolCallArg(context, params.offset), } case 'create_task': return { @@ -552,21 +591,22 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n return { ...baseParams, workspace: params.workspace, - text: params.searchText, + text: params.searchText || toolCallArg(context, params.text), assignee: params.assignee, projects: projectsArray, completed: completedValue, + limit: toLimit(pickLimit(params.searchTasks_limit, params.limit)), } case 'add_comment': return { ...baseParams, taskGid: params.taskGid, - text: params.commentText, + text: params.commentText || toolCallArg(context, params.text), } case 'create_subtask': return { ...baseParams, - taskGid: params.subtaskParentGid, + taskGid: params.subtaskParentGid || toolCallArg(context, params.taskGid), name: params.name, notes: params.notes, assignee: params.assignee, @@ -581,17 +621,12 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n return { ...baseParams, taskGid: params.taskGid, - followers: params.followers - ? params.followers - .split(',') - .map((f: string) => f.trim()) - .filter((f: string) => f.length > 0) - : [], + followers: splitGids(params.followers) ?? [], } case 'create_project': return { ...baseParams, - workspace: params.createProject_workspace, + workspace: params.createProject_workspace || toolCallArg(context, params.workspace), name: params.name, notes: params.notes, } @@ -629,6 +664,8 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n getTasks_workspace: { type: 'string', description: 'Workspace GID for getting tasks' }, getTasks_project: { type: 'string', description: 'Project GID filter for getting tasks' }, getTasks_limit: { type: 'string', description: 'Limit for getting tasks' }, + getTasks_offset: { type: 'string', description: 'Pagination offset for getting tasks' }, + searchTasks_limit: { type: 'string', description: 'Limit for searching tasks' }, name: { type: 'string', description: 'Task name' }, notes: { type: 'string', description: 'Task notes' }, assignee: { type: 'string', description: 'Assignee user GID' }, @@ -660,6 +697,10 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n modified_at: { type: 'string', description: 'Last modified timestamp' }, permalink_url: { type: 'string', description: 'URL to the resource in Asana' }, tasks: { type: 'json', description: 'Array of tasks' }, + next_page: { + type: 'json', + description: 'Pagination info when getting multiple tasks (offset, path, uri)', + }, projects: { type: 'json', description: 'Array of projects' }, workspaces: { type: 'json', description: 'Array of workspaces' }, sections: { type: 'json', description: 'Array of sections' }, diff --git a/apps/sim/blocks/blocks/attio.test.ts b/apps/sim/blocks/blocks/attio.test.ts new file mode 100644 index 00000000000..5c37caa5076 --- /dev/null +++ b/apps/sim/blocks/blocks/attio.test.ts @@ -0,0 +1,20 @@ +import { triggersMock } from '@sim/testing/mocks/triggers.mock' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/triggers', () => triggersMock) + +import { AttioBlock } from '@/blocks/blocks/attio' + +describe('Attio agent operation resolution', () => { + it('maps the selected list when the operation arrives only in the tool-call context', () => { + const mapper = AttioBlock.tools.config?.params + if (!mapper) throw new Error('Attio block has no params mapper') + + const mapped = mapper( + { oauthCredential: 'cred', listIdOrSlug: 'list-1' }, + { operation: 'get_list' } + ) + + expect(mapped.list).toBe('list-1') + }) +}) diff --git a/apps/sim/blocks/blocks/attio.ts b/apps/sim/blocks/blocks/attio.ts index 0fcea1b3306..37a9aff63ab 100644 --- a/apps/sim/blocks/blocks/attio.ts +++ b/apps/sim/blocks/blocks/attio.ts @@ -1609,11 +1609,11 @@ Record reference: {"allowed_objects": ["people", "companies"]}`, ], config: { tool: (params) => `attio_${params.operation}`, - params: (params) => { + params: (params, context) => { const cleanParams: Record = { oauthCredential: params.oauthCredential, } - const op = params.operation + const op = params.operation ?? context?.operation // Record params — each field is only sent for the operations whose subBlock condition // actually exposes it, so a stale value left over from switching operations never leaks diff --git a/apps/sim/blocks/blocks/box.ts b/apps/sim/blocks/blocks/box.ts index 4a39d831d2c..c9d3113b371 100644 --- a/apps/sim/blocks/blocks/box.ts +++ b/apps/sim/blocks/blocks/box.ts @@ -3,7 +3,7 @@ import { BoxCompanyIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, toolCallArg } from '@/blocks/utils' /** Canonical pair for the upload payload: file picker in basic mode, file reference in advanced. */ const UPLOAD_FILE_FIELD = ['uploadFile', 'fileRef'] as const @@ -327,6 +327,21 @@ export const BoxBlock = { condition: { field: 'operation', value: 'list_folder_items' }, mode: 'advanced', }, + { + id: 'usemarker', + title: 'Use Marker Pagination', + type: 'switch', + condition: { field: 'operation', value: 'list_folder_items' }, + mode: 'advanced', + }, + { + id: 'folderMarker', + title: 'Pagination Marker', + type: 'short-input', + placeholder: 'nextMarker from a previous response', + condition: { field: 'operation', value: 'list_folder_items' }, + mode: 'advanced', + }, // Sign Request fields { @@ -514,12 +529,13 @@ export const BoxBlock = { } return `box_${op}` }, - params: (params) => { + params: (params, context) => { const normalizedFile = normalizeFileInput(params.file, { single: true }) if (normalizedFile) { params.file = normalizedFile } - const { credential, operation, ...rest } = params + const { credential, operation: storedOperation, ...rest } = params + const operation = storedOperation ?? context?.operation const baseParams: Record = { accessToken: credential, @@ -542,9 +558,11 @@ export const BoxBlock = { if (rest.offset) baseParams.offset = Number(rest.offset) if (rest.sort) baseParams.sort = rest.sort if (rest.direction) baseParams.direction = rest.direction + baseParams.usemarker = rest.usemarker === true || rest.usemarker === 'true' || undefined + baseParams.marker = rest.folderMarker || toolCallArg(context, rest.marker) break case 'create_folder': - baseParams.name = rest.folderName + baseParams.name = rest.folderName || toolCallArg(context, rest.name) baseParams.parentFolderId = rest.parentFolderId break case 'delete_folder': @@ -567,7 +585,7 @@ export const BoxBlock = { case 'update_file': baseParams.fileId = rest.fileId if (rest.newName) baseParams.name = rest.newName - if (rest.description !== undefined) baseParams.description = rest.description + if (typeof rest.description === 'string') baseParams.description = rest.description if (rest.moveToFolderId) baseParams.parentFolderId = rest.moveToFolderId if (rest.tags) baseParams.tags = rest.tags break diff --git a/apps/sim/blocks/blocks/clickup.test.ts b/apps/sim/blocks/blocks/clickup.test.ts new file mode 100644 index 00000000000..c8fcf38d4fc --- /dev/null +++ b/apps/sim/blocks/blocks/clickup.test.ts @@ -0,0 +1,31 @@ +import { triggersMock } from '@sim/testing/mocks/triggers.mock' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/triggers', () => triggersMock) + +import { ClickUpBlock } from '@/blocks/blocks/clickup' +import type { BlockToolParamsContext } from '@/blocks/types' + +const mapParams = ( + inputs: Record, + context?: BlockToolParamsContext +): Record => ClickUpBlock.tools.config?.params?.(inputs, context) ?? {} + +/** On an agent call the stored operation reaches the mapper only on `context`, never in the args. */ +describe('ClickUp agent tool calls', () => { + it('maps the workspace picked on the tool to workspaceId', () => { + expect(mapParams({ teamId: 'team-1' }, { operation: 'search_tasks' }).workspaceId).toBe( + 'team-1' + ) + }) + + it('keeps the model workspaceId when no workspace is picked', () => { + expect(mapParams({ workspaceId: 'team-2' }, { operation: 'get_spaces' }).workspaceId).toBe( + 'team-2' + ) + }) + + it('maps the picked workspace on the canvas exactly as before', () => { + expect(mapParams({ operation: 'search_tasks', teamId: 'team-1' }).workspaceId).toBe('team-1') + }) +}) diff --git a/apps/sim/blocks/blocks/clickup.ts b/apps/sim/blocks/blocks/clickup.ts index a21aa70baaa..b958b465af4 100644 --- a/apps/sim/blocks/blocks/clickup.ts +++ b/apps/sim/blocks/blocks/clickup.ts @@ -2,7 +2,12 @@ import { ClickUpIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { + isToolCall, + normalizeFileInput, + parseOptionalBooleanInput, + toolCallArg, +} from '@/blocks/utils' import { getTrigger } from '@/triggers' const TIMESTAMP_WAND_PROMPT = `Generate a Unix timestamp in milliseconds based on the user's description. @@ -13,12 +18,17 @@ Examples: Return ONLY the numeric timestamp in milliseconds - no explanations, no quotes, no extra text.` +/** + * Splits a comma-separated block input into trimmed values. An array (the shape a model + * sends for the tool's array params on a tool call) is accepted as already split. + */ function splitCommaSeparated(value: unknown): string[] | undefined { - if (typeof value !== 'string') return undefined - const parts = value - .split(',') - .map((part) => part.trim()) - .filter((part) => part.length > 0) + const rawParts = Array.isArray(value) + ? value.filter((part) => typeof part === 'string' || typeof part === 'number').map(String) + : typeof value === 'string' + ? value.split(',') + : [] + const parts = rawParts.map((part) => part.trim()).filter((part) => part.length > 0) return parts.length > 0 ? parts : undefined } @@ -880,6 +890,74 @@ export const ClickUpBlock: BlockConfig = { generationType: 'timestamp', }, }, + { + id: 'dateCreatedGt', + title: 'Created After', + type: 'short-input', + mode: 'advanced', + placeholder: 'Unix timestamp in milliseconds', + condition: { + field: 'operation', + value: ['get_tasks', 'search_tasks'], + }, + wandConfig: { + enabled: true, + prompt: TIMESTAMP_WAND_PROMPT, + placeholder: 'Describe the earliest creation date (e.g., "start of this week")...', + generationType: 'timestamp', + }, + }, + { + id: 'dateCreatedLt', + title: 'Created Before', + type: 'short-input', + mode: 'advanced', + placeholder: 'Unix timestamp in milliseconds', + condition: { + field: 'operation', + value: ['get_tasks', 'search_tasks'], + }, + wandConfig: { + enabled: true, + prompt: TIMESTAMP_WAND_PROMPT, + placeholder: 'Describe the latest creation date (e.g., "yesterday")...', + generationType: 'timestamp', + }, + }, + { + id: 'dateUpdatedGt', + title: 'Updated After', + type: 'short-input', + mode: 'advanced', + placeholder: 'Unix timestamp in milliseconds', + condition: { + field: 'operation', + value: ['get_tasks', 'search_tasks'], + }, + wandConfig: { + enabled: true, + prompt: TIMESTAMP_WAND_PROMPT, + placeholder: 'Describe the earliest update time (e.g., "1 hour ago")...', + generationType: 'timestamp', + }, + }, + { + id: 'dateUpdatedLt', + title: 'Updated Before', + type: 'short-input', + mode: 'advanced', + placeholder: 'Unix timestamp in milliseconds', + condition: { + field: 'operation', + value: ['get_tasks', 'search_tasks'], + }, + wandConfig: { + enabled: true, + prompt: TIMESTAMP_WAND_PROMPT, + placeholder: 'Describe the latest update time (e.g., "now")...', + generationType: 'timestamp', + }, + }, { id: 'listIds', title: 'List IDs', @@ -1398,8 +1476,10 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr ], config: { tool: (params) => `clickup_${params.operation}`, - params: (params) => { - const { oauthCredential, operation } = params + params: (params, context) => { + const { oauthCredential } = params + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const operation = params.operation ?? context?.operation const baseParams = { accessToken: oauthCredential?.accessToken, @@ -1408,6 +1488,25 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr const priorityValue = params.priority && params.priority !== 'none' ? Number(params.priority) : undefined + /** + * On a tool call the model's args arrive under the tool's param names, so a value + * collected by a differently named sub-block falls back to the model's arg there. + */ + const workspaceId = params.teamId || toolCallArg(context, params.workspaceId) || undefined + const resolvedFlag = + params.resolvedAction === 'resolve' + ? true + : params.resolvedAction === 'unresolve' + ? false + : toolCallArg(context, parseOptionalBooleanInput(params.resolved)) + const billable = + billableFromAction(params.billableAction) ?? + toolCallArg(context, parseOptionalBooleanInput(params.billable)) + const timerTaskId = params.timerTaskId || toolCallArg(context, params.taskId) || undefined + const entryDescription = + params.entryDescription || toolCallArg(context, params.description) || undefined + const listSpaceId = params.listSpaceId || toolCallArg(context, params.spaceId) || undefined + switch (operation) { case 'create_task': return { @@ -1471,7 +1570,7 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr ? true : params.archiveAction === 'unarchive' ? false - : undefined, + : toolCallArg(context, parseOptionalBooleanInput(params.archived)), } case 'delete_task': return { @@ -1494,11 +1593,15 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr tags: splitCommaSeparated(params.tags), dueDateGt: optionalNumber(params.dueDateGt), dueDateLt: optionalNumber(params.dueDateLt), + dateCreatedGt: optionalNumber(params.dateCreatedGt), + dateCreatedLt: optionalNumber(params.dateCreatedLt), + dateUpdatedGt: optionalNumber(params.dateUpdatedGt), + dateUpdatedLt: optionalNumber(params.dateUpdatedLt), } case 'search_tasks': return { ...baseParams, - workspaceId: params.teamId, + workspaceId, page: optionalNumber(params.page), orderBy: params.orderBy && params.orderBy !== 'none' ? params.orderBy : undefined, reverse: params.reverse ? true : undefined, @@ -1513,13 +1616,19 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr tags: splitCommaSeparated(params.tags), dueDateGt: optionalNumber(params.dueDateGt), dueDateLt: optionalNumber(params.dueDateLt), + dateCreatedGt: optionalNumber(params.dateCreatedGt), + dateCreatedLt: optionalNumber(params.dateCreatedLt), + dateUpdatedGt: optionalNumber(params.dateUpdatedGt), + dateUpdatedLt: optionalNumber(params.dateUpdatedLt), } case 'create_comment': return { ...baseParams, taskId: params.taskId, commentText: params.commentText, - assignee: optionalNumber(params.commentAssignee), + assignee: + optionalNumber(params.commentAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), notifyAll: params.notifyAll ? true : undefined, } case 'get_comments': @@ -1534,13 +1643,10 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr ...baseParams, commentId: params.commentId, commentText: params.commentText || undefined, - assignee: optionalNumber(params.commentAssignee), - resolved: - params.resolvedAction === 'resolve' - ? true - : params.resolvedAction === 'unresolve' - ? false - : undefined, + assignee: + optionalNumber(params.commentAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), + resolved: resolvedFlag, } case 'delete_comment': return { @@ -1592,7 +1698,7 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr case 'get_spaces': return { ...baseParams, - workspaceId: params.teamId, + workspaceId, archived: params.archived ? true : undefined, } case 'get_folders': @@ -1605,7 +1711,8 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr return { ...baseParams, folderId: params.listParent === 'space' ? undefined : params.folderId || undefined, - spaceId: params.listParent === 'space' ? params.listSpaceId || undefined : undefined, + spaceId: + params.listParent === 'space' ? listSpaceId : toolCallArg(context, listSpaceId), archived: params.archived ? true : undefined, } case 'create_folder': @@ -1618,7 +1725,8 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr return { ...baseParams, folderId: params.listParent === 'space' ? undefined : params.folderId || undefined, - spaceId: params.listParent === 'space' ? params.listSpaceId || undefined : undefined, + spaceId: + params.listParent === 'space' ? listSpaceId : toolCallArg(context, listSpaceId), name: params.name, content: params.content || undefined, markdownContent: params.markdownContent || undefined, @@ -1628,7 +1736,10 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr ...baseParams, taskId: params.taskId, fieldId: params.fieldId, - value: parseCustomFieldValue(params.fieldValue), + value: + isToolCall(context) && (params.fieldValue === undefined || params.fieldValue === '') + ? params.value + : parseCustomFieldValue(params.fieldValue), } case 'remove_custom_field_value': return { @@ -1659,7 +1770,9 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr ...baseParams, checklistId: params.checklistId, name: params.name, - assignee: optionalNumber(params.itemAssignee), + assignee: + optionalNumber(params.itemAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), } case 'update_checklist_item': return { @@ -1667,14 +1780,11 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr checklistId: params.checklistId, checklistItemId: params.checklistItemId, name: params.name || undefined, - assignee: optionalNumber(params.itemAssignee), - resolved: - params.resolvedAction === 'resolve' - ? true - : params.resolvedAction === 'unresolve' - ? false - : undefined, - parent: params.itemParent || undefined, + assignee: + optionalNumber(params.itemAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), + resolved: resolvedFlag, + parent: params.itemParent || toolCallArg(context, params.parent) || undefined, } case 'delete_checklist_item': return { @@ -1685,73 +1795,96 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr case 'get_time_entries': return { ...baseParams, - workspaceId: params.teamId, - startDate: optionalNumber(params.timeStartDate), - endDate: optionalNumber(params.timeEndDate), - assignee: params.timerAssignee || undefined, + workspaceId, + startDate: + optionalNumber(params.timeStartDate) ?? + toolCallArg(context, optionalNumber(params.startDate)), + endDate: + optionalNumber(params.timeEndDate) ?? + toolCallArg(context, optionalNumber(params.endDate)), + assignee: params.timerAssignee || toolCallArg(context, params.assignee) || undefined, taskId: - params.timeLocationType === 'task' ? params.timeLocationId || undefined : undefined, + (params.timeLocationType === 'task' ? params.timeLocationId : undefined) || + toolCallArg(context, params.taskId) || + undefined, listId: - params.timeLocationType === 'list' ? params.timeLocationId || undefined : undefined, + (params.timeLocationType === 'list' ? params.timeLocationId : undefined) || + toolCallArg(context, params.listId) || + undefined, folderId: - params.timeLocationType === 'folder' - ? params.timeLocationId || undefined - : undefined, + (params.timeLocationType === 'folder' ? params.timeLocationId : undefined) || + toolCallArg(context, params.folderId) || + undefined, spaceId: - params.timeLocationType === 'space' - ? params.timeLocationId || undefined - : undefined, + (params.timeLocationType === 'space' ? params.timeLocationId : undefined) || + toolCallArg(context, params.spaceId) || + undefined, includeTaskTags: params.includeTaskTags ? true : undefined, includeLocationNames: params.includeLocationNames ? true : undefined, } case 'create_time_entry': return { ...baseParams, - workspaceId: params.teamId, - start: optionalNumber(params.entryStart), - duration: optionalNumber(params.entryDuration), - description: params.entryDescription || undefined, - billable: billableFromAction(params.billableAction), - taskId: params.timerTaskId || undefined, - assignee: optionalNumber(params.entryAssignee), + workspaceId, + start: + optionalNumber(params.entryStart) ?? + toolCallArg(context, optionalNumber(params.start)), + duration: + optionalNumber(params.entryDuration) ?? + toolCallArg(context, optionalNumber(params.duration)), + description: entryDescription, + billable, + taskId: timerTaskId, + assignee: + optionalNumber(params.entryAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), } case 'update_time_entry': return { ...baseParams, - workspaceId: params.teamId, + workspaceId, timerId: params.timerId, - description: params.entryDescription || undefined, - start: optionalNumber(params.entryStart), - end: optionalNumber(params.entryEnd), - duration: optionalNumber(params.entryDuration), - taskId: params.timerTaskId || undefined, - billable: billableFromAction(params.billableAction), + description: entryDescription, + start: + optionalNumber(params.entryStart) ?? + toolCallArg(context, optionalNumber(params.start)), + end: + optionalNumber(params.entryEnd) ?? toolCallArg(context, optionalNumber(params.end)), + duration: + optionalNumber(params.entryDuration) ?? + toolCallArg(context, optionalNumber(params.duration)), + taskId: timerTaskId, + billable, } case 'delete_time_entry': return { ...baseParams, - workspaceId: params.teamId, + workspaceId, timerId: params.timerId, } case 'start_timer': return { ...baseParams, - workspaceId: params.teamId, - taskId: params.timerTaskId || undefined, - description: params.entryDescription || undefined, - billable: billableFromAction(params.billableAction), - tags: splitCommaSeparated(params.timerTags), + workspaceId, + taskId: timerTaskId, + description: entryDescription, + billable, + tags: + splitCommaSeparated(params.timerTags) ?? + toolCallArg(context, splitCommaSeparated(params.tags)), } case 'stop_timer': return { ...baseParams, - workspaceId: params.teamId, + workspaceId, } case 'get_running_timer': return { ...baseParams, - workspaceId: params.teamId, - assignee: optionalNumber(params.entryAssignee), + workspaceId, + assignee: + optionalNumber(params.entryAssignee) ?? + toolCallArg(context, optionalNumber(params.assignee)), } default: return baseParams @@ -1811,6 +1944,22 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr statuses: { type: 'string', description: 'Comma-separated status filters' }, dueDateGt: { type: 'string', description: 'Only tasks due after this Unix ms timestamp' }, dueDateLt: { type: 'string', description: 'Only tasks due before this Unix ms timestamp' }, + dateCreatedGt: { + type: 'string', + description: 'Only tasks created after this Unix ms timestamp', + }, + dateCreatedLt: { + type: 'string', + description: 'Only tasks created before this Unix ms timestamp', + }, + dateUpdatedGt: { + type: 'string', + description: 'Only tasks updated after this Unix ms timestamp', + }, + dateUpdatedLt: { + type: 'string', + description: 'Only tasks updated before this Unix ms timestamp', + }, listIds: { type: 'string', description: 'Comma-separated list ID filters' }, spaceIds: { type: 'string', description: 'Comma-separated space ID filters' }, folderIds: { type: 'string', description: 'Comma-separated folder ID filters' }, @@ -1864,6 +2013,7 @@ Return ONLY the value (plain string, number, or JSON) - no explanations, no extr outputs: { task: { type: 'json', description: 'Task details' }, tasks: { type: 'json', description: 'Array of tasks' }, + lastPage: { type: 'boolean', description: 'Whether Get Tasks returned the last page' }, comments: { type: 'json', description: 'Array of comments' }, id: { type: 'string', description: 'ID of the affected resource' }, histId: { type: 'string', description: 'History ID of the created comment' }, diff --git a/apps/sim/blocks/blocks/docusign.test.ts b/apps/sim/blocks/blocks/docusign.test.ts new file mode 100644 index 00000000000..75a3f62e05c --- /dev/null +++ b/apps/sim/blocks/blocks/docusign.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' +import { DocuSignBlock } from '@/blocks/blocks/docusign' + +function runCanvas(params: Record) { + const mapper = DocuSignBlock.tools.config?.params + if (!mapper) throw new Error('DocuSign block has no params mapper') + return { ...params, ...mapper(params) } +} + +describe('DocuSign List Envelopes status filter', () => { + it('does not filter by the stored Send Envelope status when the list filter is All', () => { + const merged = runCanvas({ + operation: 'list_envelopes', + envelopeStatus: 'sent', + listEnvelopeStatus: '', + }) + + expect(merged.envelopeStatus).toBeUndefined() + }) + + it('sends the chosen list filter', () => { + const merged = runCanvas({ + operation: 'list_envelopes', + envelopeStatus: 'sent', + listEnvelopeStatus: 'completed', + }) + + expect(merged.envelopeStatus).toBe('completed') + }) +}) diff --git a/apps/sim/blocks/blocks/docusign.ts b/apps/sim/blocks/blocks/docusign.ts index 1da535cd528..9efc2329b38 100644 --- a/apps/sim/blocks/blocks/docusign.ts +++ b/apps/sim/blocks/blocks/docusign.ts @@ -2,7 +2,7 @@ import { DocuSignIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, toolCallArg } from '@/blocks/utils' const DOCUMENT_FIELD = ['uploadDocument', 'documentRef'] as const @@ -287,7 +287,15 @@ export const DocuSignBlock: BlockConfig = { id: 'count', title: 'Max Results', type: 'short-input', - placeholder: '25', + placeholder: '100', + condition: { field: 'operation', value: ['list_envelopes', 'list_templates'] }, + mode: 'advanced', + }, + { + id: 'startPosition', + title: 'Start Position', + type: 'short-input', + placeholder: '0', condition: { field: 'operation', value: ['list_envelopes', 'list_templates'] }, mode: 'advanced', }, @@ -306,7 +314,7 @@ export const DocuSignBlock: BlockConfig = { ], config: { tool: (params) => `docusign_${params.operation}`, - params: (params) => { + params: (params, context) => { const { oauthCredential, operation, documentFile, listEnvelopeStatus, ...rest } = params const cleanParams: Record = { @@ -318,8 +326,14 @@ export const DocuSignBlock: BlockConfig = { cleanParams.file = file } - if (listEnvelopeStatus && operation === 'list_envelopes') { - cleanParams.envelopeStatus = listEnvelopeStatus + /** + * The Send/Template status dropdown stores `sent` on every new block, and the executor + * merges raw inputs under this result, so the list filter is always assigned explicitly: + * otherwise that stored `sent` reached List Envelopes and "All" listed only sent envelopes. + */ + if ((operation ?? context?.operation) === 'list_envelopes') { + cleanParams.envelopeStatus = + listEnvelopeStatus || toolCallArg(context, rest.envelopeStatus) || undefined } if (operation === 'create_from_template') { @@ -359,6 +373,7 @@ export const DocuSignBlock: BlockConfig = { toDate: { type: 'string', description: 'End date filter' }, searchText: { type: 'string', description: 'Search text filter' }, count: { type: 'string', description: 'Max results to return' }, + startPosition: { type: 'string', description: 'Zero-based index of the first result' }, }, outputs: { envelopeId: { type: 'string', description: 'Envelope ID' }, @@ -404,6 +419,18 @@ export const DocuSignBlock: BlockConfig = { fileName: { type: 'string', description: 'Document file name' }, totalSetSize: { type: 'number', description: 'Total matching results' }, resultSetSize: { type: 'number', description: 'Results returned in this response' }, + startPosition: { + type: 'number', + description: 'Zero-based index of the first result in this page', + }, + endPosition: { + type: 'number', + description: 'Zero-based index of the last result in this page', + }, + nextUri: { + type: 'string', + description: 'URI of the next page of results (null on the last page)', + }, }, } diff --git a/apps/sim/blocks/blocks/dropbox.test.ts b/apps/sim/blocks/blocks/dropbox.test.ts new file mode 100644 index 00000000000..2ac157a70d3 --- /dev/null +++ b/apps/sim/blocks/blocks/dropbox.test.ts @@ -0,0 +1,38 @@ +import { triggersMock } from '@sim/testing/mocks/triggers.mock' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/triggers', () => triggersMock) + +import { DropboxBlock } from '@/blocks/blocks/dropbox' + +function requiredFor(subBlockId: string, values: Record) { + const subBlock = DropboxBlock.subBlocks.find( + (candidate) => candidate.id === subBlockId && typeof candidate.required === 'function' + ) + if (!subBlock || typeof subBlock.required !== 'function') { + throw new Error(`${subBlockId} has no dynamic required condition`) + } + return subBlock.required(values) +} + +describe('Dropbox cursor continuation', () => { + it('requires a folder path only until a list folder cursor is set', () => { + expect(requiredFor('path', {})).toEqual({ field: 'operation', value: 'dropbox_list_folder' }) + expect(requiredFor('path', { listFolderCursor: ' ' })).toEqual({ + field: 'operation', + value: 'dropbox_list_folder', + }) + expect(requiredFor('path', { listFolderCursor: 'cursor-1' })).toEqual({ + field: 'operation', + value: [], + }) + }) + + it('requires a search query only until a search cursor is set', () => { + expect(requiredFor('query', {})).toEqual({ field: 'operation', value: 'dropbox_search' }) + expect(requiredFor('query', { searchCursor: 'cursor-1' })).toEqual({ + field: 'operation', + value: [], + }) + }) +}) diff --git a/apps/sim/blocks/blocks/dropbox.ts b/apps/sim/blocks/blocks/dropbox.ts index 55b195d283c..5ef0717c153 100644 --- a/apps/sim/blocks/blocks/dropbox.ts +++ b/apps/sim/blocks/blocks/dropbox.ts @@ -3,7 +3,7 @@ import { DropboxIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, toolCallArg } from '@/blocks/utils' /* * Canonical basic/advanced pair for the upload source, shared by the card @@ -175,7 +175,10 @@ export const DropboxBlock = { type: 'short-input', placeholder: '/ (root) or /folder', condition: { field: 'operation', value: 'dropbox_list_folder' }, - required: true, + required: (values) => + String(values?.listFolderCursor ?? '').trim() + ? { field: 'operation', value: [] } + : { field: 'operation', value: 'dropbox_list_folder' }, }, { id: 'recursive', @@ -190,6 +193,14 @@ export const DropboxBlock = { placeholder: '500', condition: { field: 'operation', value: 'dropbox_list_folder' }, }, + { + id: 'listFolderCursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Cursor from a previous call, to fetch the next page', + condition: { field: 'operation', value: 'dropbox_list_folder' }, + mode: 'advanced', + }, { id: 'path', title: 'Folder Path', @@ -324,7 +335,10 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`, type: 'short-input', placeholder: 'Enter search term...', condition: { field: 'operation', value: 'dropbox_search' }, - required: true, + required: (values) => + String(values?.searchCursor ?? '').trim() + ? { field: 'operation', value: [] } + : { field: 'operation', value: 'dropbox_search' }, }, { id: 'path', @@ -347,6 +361,14 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`, placeholder: '100', condition: { field: 'operation', value: 'dropbox_search' }, }, + { + id: 'searchCursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Cursor from a previous call, to fetch the next page', + condition: { field: 'operation', value: 'dropbox_search' }, + mode: 'advanced', + }, { id: 'path', title: 'Path', @@ -459,8 +481,15 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`, return 'dropbox_upload' } }, - params: (params) => { + params: (params, context) => { const result: Record = {} + const operation = params.operation ?? context?.operation + // `cursor` is List Shared Links' field; List Folder and Search page with their own + if (operation === 'dropbox_list_folder') { + result.cursor = params.listFolderCursor || toolCallArg(context, params.cursor) + } else if (operation === 'dropbox_search') { + result.cursor = params.searchCursor || toolCallArg(context, params.cursor) + } if (params.limit) result.limit = Number(params.limit) if (params.maxResults) result.maxResults = Number(params.maxResults) const normalizedFile = normalizeFileInput(params.file, { single: true }) @@ -494,6 +523,11 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`, maxResults: { type: 'number', description: 'Maximum search results' }, directOnly: { type: 'boolean', description: 'Only return direct links, not parent folders' }, cursor: { type: 'string', description: 'Fetch the next page of shared links (pagination)' }, + listFolderCursor: { + type: 'string', + description: 'Fetch the next page of folder entries (pagination)', + }, + searchCursor: { type: 'string', description: 'Fetch the next page of search results' }, beforeRev: { type: 'string', description: 'Fetch revisions before this one (pagination)' }, rev: { type: 'string', description: 'Revision identifier to restore' }, }, diff --git a/apps/sim/blocks/blocks/hubspot.ts b/apps/sim/blocks/blocks/hubspot.ts index d99899c5f70..4909c3b0516 100644 --- a/apps/sim/blocks/blocks/hubspot.ts +++ b/apps/sim/blocks/blocks/hubspot.ts @@ -2,6 +2,7 @@ import { HubspotIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { toolCallArg } from '@/blocks/utils' import { getTrigger } from '@/triggers' export const HubSpotBlock: BlockConfig = { @@ -1631,10 +1632,10 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no throw new Error(`Unknown operation: ${params.operation}`) } }, - params: (params) => { + params: (params, context) => { const { oauthCredential, - operation, + operation: storedOperation, propertiesToSet, properties, searchProperties, @@ -1646,6 +1647,7 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no archived, ...rest } = params + const operation = storedOperation ?? context?.operation const cleanParams: Record = { oauthCredential, @@ -1698,8 +1700,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no 'search_line_items', 'search_quotes', ] - if (searchProperties && searchOps.includes(operation as string)) { - cleanParams.properties = searchProperties + if (searchOps.includes(operation as string)) { + cleanParams.properties = searchProperties || toolCallArg(context, properties) } if (filterGroups && searchOps.includes(operation as string)) { diff --git a/apps/sim/blocks/blocks/intercom.test.ts b/apps/sim/blocks/blocks/intercom.test.ts new file mode 100644 index 00000000000..8e761a8c81a --- /dev/null +++ b/apps/sim/blocks/blocks/intercom.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest' +import { IntercomV2Block } from '@/blocks/blocks/intercom' +import type { BlockToolParamsContext } from '@/blocks/types' + +function mapParams(params: Record, context?: BlockToolParamsContext) { + const mapper = IntercomV2Block.tools.config?.params + if (!mapper) throw new Error('Intercom block has no params mapper') + return mapper(params, context) +} + +describe('Intercom snooze timestamps', () => { + it('sends a positive whole Unix timestamp as a number', () => { + expect( + mapParams({ + operation: 'snooze_conversation', + conversationId: 'c1', + admin_id: 'a1', + snoozed_until: '1767225600', + }).snoozed_until + ).toBe(1767225600) + }) + + it.each(['0', '-5', ' ', '1.5', 0])('rejects %j as a snooze time', (snoozedUntil) => { + expect(() => + mapParams({ + operation: 'snooze_conversation', + conversationId: 'c1', + admin_id: 'a1', + snoozed_until: snoozedUntil, + }) + ).toThrow('positive whole Unix timestamp') + }) +}) + +describe('Intercom agent operation resolution', () => { + it('keeps a boolean disable_notifications when the operation arrives only in the context', () => { + const mapped = mapParams( + { ticket_type_id: 't1', disable_notifications: true }, + { operation: 'create_ticket' } + ) + + expect(mapped.disable_notifications).toBe(true) + }) +}) + +describe('Intercom ticket snooze', () => { + it('validates the model snoozed_until on an update_ticket tool call', () => { + const mapped = mapParams( + { ticket_id: 't1', snoozed_until: '1767225600' }, + { operation: 'update_ticket' } + ) + expect(mapped.snoozed_until).toBe(1767225600) + expect(() => + mapParams({ ticket_id: 't1', snoozed_until: '-1' }, { operation: 'update_ticket' }) + ).toThrow('positive whole Unix timestamp') + }) + + it('does not send a stale conversation snooze value with a canvas ticket update', () => { + const inputs = { operation: 'update_ticket', ticket_id: 't1', snoozed_until: '5' } + const sent = { ...inputs, ...mapParams(inputs) } + expect(sent.snoozed_until).toBeUndefined() + }) +}) + +describe('Intercom role defaults', () => { + const role = IntercomV2Block.subBlocks.find((subBlock) => subBlock.id === 'role') + + it('leads with No change everywhere except a known Create Contact', () => { + const options = role?.options + if (typeof options !== 'function') throw new Error('role options are static') + expect(options({ values: {} })[0]?.id).toBe('') + expect(options({ values: { operation: 'update_contact' } })[0]?.id).toBe('') + expect(options({ values: { operation: 'create_contact' } })[0]?.id).toBe('lead') + expect(options()[0]?.id).toBe('lead') + }) +}) diff --git a/apps/sim/blocks/blocks/intercom.ts b/apps/sim/blocks/blocks/intercom.ts index 75a108c4571..6121c2c4e32 100644 --- a/apps/sim/blocks/blocks/intercom.ts +++ b/apps/sim/blocks/blocks/intercom.ts @@ -1,7 +1,8 @@ +import { toBooleanFromInput } from '@sim/utils/coerce' import { IntercomIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { createVersionedToolSelector, withSavedCredential } from '@/blocks/utils' +import { createVersionedToolSelector, toolCallArg, withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' /** @@ -17,6 +18,166 @@ const EVENT_ACTOR_FIELD = ['event_email', 'event_user_id', 'event_contact_id'] a /** What an outbound message shows: its subject when set, otherwise its body. */ const MESSAGE_PREVIEW_FIELD = ['subject', 'body'] as const +/** True when a field holds a value, so a numeric 0 still reaches validation. */ +function hasInput(value: unknown): boolean { + return value !== undefined && value !== null && value !== '' +} + +/** Intercom requires `snoozed_until` as a positive whole Unix timestamp in seconds. */ +function toSnoozeTimestamp(value: unknown): number { + const text = String(value).trim() + const timestamp = Number(text) + if (text === '' || !Number.isSafeInteger(timestamp) || timestamp <= 0) { + throw new Error('Snooze until must be a positive whole Unix timestamp in seconds') + } + return timestamp +} + +type IntercomParamsMapper = NonNullable['params']> + +/** Maps Intercom block fields to tool params; shared by every Intercom block version. */ +const mapIntercomParams: IntercomParamsMapper = (params, context) => { + const { + operation: storedOperation, + message_type_msg, + company_name, + contact_company_id, + reply_created_at, + ticket_company_id, + ticket_created_at, + message_created_at, + include_translations, + disable_notifications, + close_body, + assign_body, + tag_contact_id, + attach_company_id, + update_ticket_attributes, + ticket_open, + ticket_is_shared, + ticket_snoozed_until, + snoozed_until, + ticket_assignee_id, + tag_name, + tag_id_update, + note_body, + event_user_id, + event_email, + event_contact_id, + event_metadata, + event_created_at, + ...rest + } = params + const operation = storedOperation ?? context?.operation + const cleanParams: Record = {} + + if (operation === 'create_message' && message_type_msg) { + cleanParams.message_type = message_type_msg + } + + if (operation === 'create_company' && company_name) { + cleanParams.name = company_name + } + + if ((operation === 'create_contact' || operation === 'update_contact') && contact_company_id) { + cleanParams.company_id = contact_company_id + } + + if (operation === 'reply_conversation' && reply_created_at) { + cleanParams.created_at = Number(reply_created_at) + } + + if (operation === 'create_ticket') { + if (ticket_company_id) cleanParams.company_id = ticket_company_id + if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at) + const disableNotifications = toBooleanFromInput(disable_notifications) + if (disableNotifications !== undefined) { + cleanParams.disable_notifications = disableNotifications + } + } + + if (operation === 'create_message' && message_created_at) { + cleanParams.created_at = Number(message_created_at) + } + + const includeTranslations = toBooleanFromInput(include_translations) + if (operation === 'get_conversation' && includeTranslations !== undefined) { + cleanParams.include_translations = includeTranslations + } + + if (operation === 'snooze_conversation' && hasInput(snoozed_until)) { + cleanParams.snoozed_until = toSnoozeTimestamp(snoozed_until) + } + + if (operation === 'close_conversation' && close_body) { + cleanParams.body = close_body + } + + if (operation === 'assign_conversation' && assign_body) { + cleanParams.body = assign_body + } + + if ( + [ + 'tag_contact', + 'untag_contact', + 'create_note', + 'attach_contact_to_company', + 'detach_contact_from_company', + ].includes(operation) && + tag_contact_id + ) { + cleanParams.contactId = tag_contact_id + } + + if ( + ['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) && + attach_company_id + ) { + cleanParams.companyId = attach_company_id + } + + if (operation === 'update_ticket') { + if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes + const ticketOpen = toBooleanFromInput(ticket_open) + if (ticketOpen !== undefined) cleanParams.open = ticketOpen + const ticketIsShared = toBooleanFromInput(ticket_is_shared) + if (ticketIsShared !== undefined) cleanParams.is_shared = ticketIsShared + const ticketSnoozedUntil = hasInput(ticket_snoozed_until) + ? ticket_snoozed_until + : toolCallArg(context, snoozed_until) + cleanParams.snoozed_until = hasInput(ticketSnoozedUntil) + ? toSnoozeTimestamp(ticketSnoozedUntil) + : undefined + if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id + } + + if (operation === 'create_tag') { + if (tag_name) cleanParams.name = tag_name + if (tag_id_update) cleanParams.id = tag_id_update + } + + if (operation === 'create_note' && note_body) { + cleanParams.body = note_body + } + + if (operation === 'create_event') { + if (event_user_id) cleanParams.user_id = event_user_id + if (event_email) cleanParams.email = event_email + if (event_contact_id) cleanParams.id = event_contact_id + if (event_metadata) cleanParams.metadata = event_metadata + if (event_created_at) cleanParams.created_at = Number(event_created_at) + } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + cleanParams[key] = value + } + }) + + return cleanParams +} + export const IntercomBlock: BlockConfig = { type: 'intercom', name: 'Intercom (Legacy)', @@ -179,9 +340,20 @@ export const IntercomBlock: BlockConfig = { id: 'role', title: 'Role', type: 'dropdown', - options: [ + dependsOn: ['operation'], + /** + * Every context except a known Create Contact leads with "No change" so an update does not + * resend a role; that includes agent tool rows, whose values carry no operation. `dependsOn` + * clears the value on an operation switch so it reseeds. Without block values (Copilot + * validation, catalogs) every option is listed, with "No change" last so the default stays Lead. + */ + options: (params) => [ + ...(params && params.values.operation !== 'create_contact' + ? [{ label: 'No change', id: '' }] + : []), { label: 'Lead', id: 'lead' }, { label: 'User', id: 'user' }, + ...(params ? [] : [{ label: 'No change', id: '' }]), ], condition: { field: 'operation', @@ -854,7 +1026,11 @@ Return ONLY the subject line - no explanations.`, id: 'to_type', title: 'To Type', type: 'dropdown', - options: [{ label: 'Contact', id: 'contact' }], + options: [ + { label: 'User', id: 'user' }, + { label: 'Lead', id: 'lead' }, + { label: 'Contact (legacy)', id: 'contact' }, + ], required: true, condition: { field: 'operation', @@ -864,9 +1040,9 @@ Return ONLY the subject line - no explanations.`, { id: 'to_id', title: 'To ID', - canvasNoun: 'a contact ID', + canvasNoun: 'a recipient ID', type: 'short-input', - placeholder: 'Contact ID', + placeholder: 'User, lead, or contact ID', required: true, condition: { field: 'operation', @@ -1334,143 +1510,7 @@ Return ONLY the numeric timestamp.`, throw new Error(`Unknown operation: ${params.operation}`) } }, - params: (params) => { - const { - operation, - message_type_msg, - company_name, - contact_company_id, - reply_created_at, - ticket_company_id, - ticket_created_at, - message_created_at, - include_translations, - disable_notifications, - close_body, - assign_body, - tag_contact_id, - attach_company_id, - update_ticket_attributes, - ticket_open, - ticket_is_shared, - ticket_snoozed_until, - ticket_assignee_id, - tag_name, - tag_id_update, - note_body, - event_user_id, - event_email, - event_contact_id, - event_metadata, - event_created_at, - ...rest - } = params - const cleanParams: Record = {} - - if (operation === 'create_message' && message_type_msg) { - cleanParams.message_type = message_type_msg - } - - if (operation === 'create_company' && company_name) { - cleanParams.name = company_name - } - - if ( - (operation === 'create_contact' || operation === 'update_contact') && - contact_company_id - ) { - cleanParams.company_id = contact_company_id - } - - if (operation === 'reply_conversation' && reply_created_at) { - cleanParams.created_at = Number(reply_created_at) - } - - if (operation === 'create_ticket') { - if (ticket_company_id) cleanParams.company_id = ticket_company_id - if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at) - if (disable_notifications !== undefined && disable_notifications !== '') { - cleanParams.disable_notifications = disable_notifications === 'true' - } - } - - if (operation === 'create_message' && message_created_at) { - cleanParams.created_at = Number(message_created_at) - } - - if ( - operation === 'get_conversation' && - include_translations !== undefined && - include_translations !== '' - ) { - cleanParams.include_translations = include_translations === 'true' - } - - if (operation === 'close_conversation' && close_body) { - cleanParams.body = close_body - } - - if (operation === 'assign_conversation' && assign_body) { - cleanParams.body = assign_body - } - - if ( - [ - 'tag_contact', - 'untag_contact', - 'create_note', - 'attach_contact_to_company', - 'detach_contact_from_company', - ].includes(operation) && - tag_contact_id - ) { - cleanParams.contactId = tag_contact_id - } - - if ( - ['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) && - attach_company_id - ) { - cleanParams.companyId = attach_company_id - } - - if (operation === 'update_ticket') { - if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes - if (ticket_open !== undefined && ticket_open !== '') { - cleanParams.open = ticket_open === 'true' - } - if (ticket_is_shared !== undefined && ticket_is_shared !== '') { - cleanParams.is_shared = ticket_is_shared === 'true' - } - if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until) - if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id - } - - if (operation === 'create_tag') { - if (tag_name) cleanParams.name = tag_name - if (tag_id_update) cleanParams.id = tag_id_update - } - - if (operation === 'create_note' && note_body) { - cleanParams.body = note_body - } - - if (operation === 'create_event') { - if (event_user_id) cleanParams.user_id = event_user_id - if (event_email) cleanParams.email = event_email - if (event_contact_id) cleanParams.id = event_contact_id - if (event_metadata) cleanParams.metadata = event_metadata - if (event_created_at) cleanParams.created_at = Number(event_created_at) - } - - Object.entries(rest).forEach(([key, value]) => { - if (value !== undefined && value !== null && value !== '') { - cleanParams[key] = value - } - }) - - return cleanParams - }, + params: mapIntercomParams, }, }, inputs: { @@ -1618,143 +1658,7 @@ export const IntercomV2Block: BlockConfig = { suffix: '_v2', fallbackToolId: 'intercom_create_contact_v2', }), - params: (params) => { - const { - operation, - message_type_msg, - company_name, - contact_company_id, - reply_created_at, - ticket_company_id, - ticket_created_at, - message_created_at, - include_translations, - disable_notifications, - close_body, - assign_body, - tag_contact_id, - attach_company_id, - update_ticket_attributes, - ticket_open, - ticket_is_shared, - ticket_snoozed_until, - ticket_assignee_id, - tag_name, - tag_id_update, - note_body, - event_user_id, - event_email, - event_contact_id, - event_metadata, - event_created_at, - ...rest - } = params - const cleanParams: Record = {} - - if (operation === 'create_message' && message_type_msg) { - cleanParams.message_type = message_type_msg - } - - if (operation === 'create_company' && company_name) { - cleanParams.name = company_name - } - - if ( - (operation === 'create_contact' || operation === 'update_contact') && - contact_company_id - ) { - cleanParams.company_id = contact_company_id - } - - if (operation === 'reply_conversation' && reply_created_at) { - cleanParams.created_at = Number(reply_created_at) - } - - if (operation === 'create_ticket') { - if (ticket_company_id) cleanParams.company_id = ticket_company_id - if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at) - if (disable_notifications !== undefined && disable_notifications !== '') { - cleanParams.disable_notifications = disable_notifications === 'true' - } - } - - if (operation === 'create_message' && message_created_at) { - cleanParams.created_at = Number(message_created_at) - } - - if ( - operation === 'get_conversation' && - include_translations !== undefined && - include_translations !== '' - ) { - cleanParams.include_translations = include_translations === 'true' - } - - if (operation === 'close_conversation' && close_body) { - cleanParams.body = close_body - } - - if (operation === 'assign_conversation' && assign_body) { - cleanParams.body = assign_body - } - - if ( - [ - 'tag_contact', - 'untag_contact', - 'create_note', - 'attach_contact_to_company', - 'detach_contact_from_company', - ].includes(operation) && - tag_contact_id - ) { - cleanParams.contactId = tag_contact_id - } - - if ( - ['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) && - attach_company_id - ) { - cleanParams.companyId = attach_company_id - } - - if (operation === 'update_ticket') { - if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes - if (ticket_open !== undefined && ticket_open !== '') { - cleanParams.open = ticket_open === 'true' - } - if (ticket_is_shared !== undefined && ticket_is_shared !== '') { - cleanParams.is_shared = ticket_is_shared === 'true' - } - if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until) - if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id - } - - if (operation === 'create_tag') { - if (tag_name) cleanParams.name = tag_name - if (tag_id_update) cleanParams.id = tag_id_update - } - - if (operation === 'create_note' && note_body) { - cleanParams.body = note_body - } - - if (operation === 'create_event') { - if (event_user_id) cleanParams.user_id = event_user_id - if (event_email) cleanParams.email = event_email - if (event_contact_id) cleanParams.id = event_contact_id - if (event_metadata) cleanParams.metadata = event_metadata - if (event_created_at) cleanParams.created_at = Number(event_created_at) - } - - Object.entries(rest).forEach(([key, value]) => { - if (value !== undefined && value !== null && value !== '') { - cleanParams[key] = value - } - }) - - return cleanParams - }, + params: mapIntercomParams, }, }, outputs: { diff --git a/apps/sim/blocks/blocks/mailchimp.ts b/apps/sim/blocks/blocks/mailchimp.ts index 6334eb32a2b..c2128c3711a 100644 --- a/apps/sim/blocks/blocks/mailchimp.ts +++ b/apps/sim/blocks/blocks/mailchimp.ts @@ -63,6 +63,10 @@ export const MailchimpBlock: BlockConfig = { { text: 'Delete member', field: 'subscriberEmail', core: true }, { text: 'from audience', field: 'listId' }, ], + delete_member_permanent: [ + { text: 'Permanently delete member', field: 'subscriberEmail', core: true }, + { text: 'from audience', field: 'listId' }, + ], archive_member: [ { text: 'Archive member', field: 'subscriberEmail', core: true }, { text: 'in audience', field: 'listId' }, @@ -322,6 +326,7 @@ export const MailchimpBlock: BlockConfig = { { label: 'Add or Update Member', id: 'add_or_update_member' }, { label: 'Update Member', id: 'update_member' }, { label: 'Delete Member', id: 'delete_member' }, + { label: 'Permanently Delete Member', id: 'delete_member_permanent' }, { label: 'Archive Member', id: 'archive_member' }, { label: 'Unarchive Member', id: 'unarchive_member' }, // Campaign operations @@ -425,6 +430,7 @@ export const MailchimpBlock: BlockConfig = { 'add_or_update_member', 'update_member', 'delete_member', + 'delete_member_permanent', 'archive_member', 'unarchive_member', 'get_segments', @@ -467,6 +473,7 @@ export const MailchimpBlock: BlockConfig = { 'add_or_update_member', 'update_member', 'delete_member', + 'delete_member_permanent', 'archive_member', 'unarchive_member', 'get_segments', @@ -630,19 +637,30 @@ Return ONLY the JSON object - no explanations or markdown.`, id: 'emailTypeOption', title: 'Email Type Option', type: 'dropdown', - options: [ + dependsOn: ['operation'], + /** + * Create Audience offers True/False. Every other context leads with "Keep current" so an + * update leaves the setting alone; that includes agent tool rows, where the operation is not + * in the values. `value` seeds API-built blocks per operation and returns undefined without + * one, so the editor falls back to the first option; `dependsOn` reseeds on an operation switch. + */ + options: (params) => [ + ...(params?.values.operation === 'create_audience' + ? [] + : [{ label: 'Keep current', id: '' }]), { label: 'True', id: 'true' }, { label: 'False', id: 'false' }, ], - required: { - field: 'operation', - value: ['create_audience'], - }, + value: (params) => + params?.operation === 'create_audience' + ? 'true' + : params?.operation === 'update_audience' + ? '' + : undefined, condition: { field: 'operation', value: ['create_audience', 'update_audience'], }, - value: () => 'true', }, // Member fields { @@ -656,6 +674,7 @@ Return ONLY the JSON object - no explanations or markdown.`, 'get_member', 'update_member', 'delete_member', + 'delete_member_permanent', 'archive_member', 'unarchive_member', 'get_member_tags', @@ -671,6 +690,7 @@ Return ONLY the JSON object - no explanations or markdown.`, 'get_member', 'update_member', 'delete_member', + 'delete_member_permanent', 'archive_member', 'unarchive_member', 'get_member_tags', @@ -1536,7 +1556,8 @@ Return ONLY the landing page title - no explanations.`, type: 'dropdown', options: [ { label: 'Signup', id: 'signup' }, - { label: 'Click Through', id: 'click-through' }, + { label: 'Product', id: 'product' }, + { label: 'Click Through (legacy)', id: 'click-through' }, ], required: { field: 'operation', @@ -1660,6 +1681,7 @@ Return ONLY the JSON array - no explanations or markdown.`, 'mailchimp_add_or_update_member', 'mailchimp_update_member', 'mailchimp_delete_member', + 'mailchimp_delete_member_permanent', 'mailchimp_archive_member', 'mailchimp_unarchive_member', 'mailchimp_get_campaigns', diff --git a/apps/sim/blocks/blocks/monday.test.ts b/apps/sim/blocks/blocks/monday.test.ts new file mode 100644 index 00000000000..f1e23a4bcd1 --- /dev/null +++ b/apps/sim/blocks/blocks/monday.test.ts @@ -0,0 +1,37 @@ +import { triggersMock } from '@sim/testing/mocks/triggers.mock' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/triggers', () => triggersMock) + +import { MondayBlock } from '@/blocks/blocks/monday' +import type { BlockToolParamsContext } from '@/blocks/types' + +const mapParams = ( + inputs: Record, + context?: BlockToolParamsContext +): Record => MondayBlock.tools.config?.params?.(inputs, context) ?? {} + +/** On an agent call the stored operation reaches the mapper only on `context`, never in the args. */ +describe('monday agent tool calls', () => { + it('maps the update body set on the tool to the create_update body param', () => { + expect( + mapParams({ itemId: '1', updateBody: 'Shipped' }, { operation: 'create_update' }).body + ).toBe('Shipped') + }) + + it('keeps the model body and column value when the tool fields are blank', () => { + expect(mapParams({ itemId: '1', body: 'Hi' }, { operation: 'create_update' }).body).toBe('Hi') + expect( + mapParams( + { boardId: '1', itemId: '2', columnId: 'status', value: '{"label":"Done"}' }, + { operation: 'change_column_value' } + ).value + ).toBe('{"label":"Done"}') + }) + + it('maps the canvas update body exactly as before', () => { + expect(mapParams({ operation: 'create_update', itemId: '1', updateBody: 'Shipped' }).body).toBe( + 'Shipped' + ) + }) +}) diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index 8bc3c3e8a6e..e349eb610c6 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -2,6 +2,7 @@ import { MondayIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { toolCallArg } from '@/blocks/utils' import { getTrigger } from '@/triggers' const BOARD_OPS = [ @@ -468,11 +469,19 @@ export const MondayBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', value: 'list_boards' }, }, + { + id: 'getItemsCursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Pagination cursor from previous response', + mode: 'advanced', + condition: { field: 'operation', value: 'get_items' }, + }, { id: 'cursor', title: 'Cursor', type: 'short-input', - placeholder: 'Pagination cursor from previous search', + placeholder: 'Pagination cursor from previous response', mode: 'advanced', condition: { field: 'operation', value: 'search_items' }, }, @@ -512,11 +521,13 @@ export const MondayBlock: BlockConfig = { const op = typeof params.operation === 'string' ? params.operation.trim() : 'list_boards' return `monday_${op}` }, - params: (params) => { + params: (params, context) => { const baseParams: Record = { oauthCredential: params.oauthCredential, } - const op = typeof params.operation === 'string' ? params.operation.trim() : 'list_boards' + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const rawOperation = params.operation ?? context?.operation + const op = typeof rawOperation === 'string' ? rawOperation.trim() : 'list_boards' switch (op) { case 'list_boards': @@ -535,12 +546,13 @@ export const MondayBlock: BlockConfig = { boardId: params.boardId, groupId: params.groupId || undefined, limit: params.limit ? Number(params.limit) : undefined, + cursor: params.getItemsCursor || toolCallArg(context, params.cursor) || undefined, } case 'search_items': return { ...baseParams, boardId: params.boardId, - columns: params.searchColumns, + columns: params.searchColumns || toolCallArg(context, params.columns), limit: params.limit ? Number(params.limit) : undefined, cursor: params.cursor || undefined, } @@ -565,7 +577,7 @@ export const MondayBlock: BlockConfig = { boardId: params.boardId, itemId: params.itemId, columnId: params.columnId, - value: params.columnValue, + value: params.columnValue || toolCallArg(context, params.value), createLabelsIfMissing: Boolean(params.createLabelsIfMissing), } case 'duplicate_item': @@ -580,7 +592,8 @@ export const MondayBlock: BlockConfig = { ...baseParams, boardName: params.boardName, boardKind: params.boardKind || 'public', - description: params.boardDescription || undefined, + description: + params.boardDescription || toolCallArg(context, params.description) || undefined, workspaceId: params.workspaceId || undefined, folderId: params.folderId || undefined, } @@ -616,7 +629,7 @@ export const MondayBlock: BlockConfig = { return { ...baseParams, itemId: params.itemId, - body: params.updateBody, + body: params.updateBody || toolCallArg(context, params.body), } case 'create_group': return { @@ -662,7 +675,8 @@ export const MondayBlock: BlockConfig = { groupColor: { type: 'string', description: 'Group color hex code' }, limit: { type: 'number', description: 'Maximum number of results' }, page: { type: 'number', description: 'Page number for pagination' }, - cursor: { type: 'string', description: 'Pagination cursor for search' }, + getItemsCursor: { type: 'string', description: 'Pagination cursor for get items' }, + cursor: { type: 'string', description: 'Pagination cursor for search items' }, }, outputs: { boards: { @@ -740,8 +754,8 @@ export const MondayBlock: BlockConfig = { }, cursor: { type: 'string', - description: 'Pagination cursor for fetching the next page of search results', - condition: { field: 'operation', value: 'search_items' }, + description: 'Pagination cursor for fetching the next page of items', + condition: { field: 'operation', value: ['get_items', 'search_items'] }, }, }, triggers: { diff --git a/apps/sim/blocks/blocks/notion.test.ts b/apps/sim/blocks/blocks/notion.test.ts new file mode 100644 index 00000000000..ae7d34bb2e8 --- /dev/null +++ b/apps/sim/blocks/blocks/notion.test.ts @@ -0,0 +1,27 @@ +import { triggersMock } from '@sim/testing/mocks/triggers.mock' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('@/triggers', () => triggersMock) + +import { NotionV2Block } from '@/blocks/blocks/notion' + +describe('Notion agent operation resolution', () => { + const mapper = NotionV2Block.tools.config?.params + if (!mapper) throw new Error('Notion block has no params mapper') + + it('parses page properties set in the tool row when the operation is in the context', () => { + const mapped = mapper( + { databaseId: 'db-1', properties: '{"Status":{"select":{"name":"Done"}}}' }, + { operation: 'notion_add_database_row' } + ) + + expect(mapped.properties).toEqual({ Status: { select: { name: 'Done' } } }) + }) + + it('accepts an already parsed query filter from the model', () => { + const filter = { property: 'Status', select: { equals: 'Done' } } + const mapped = mapper({ databaseId: 'db-1', filter }, { operation: 'notion_query_database' }) + + expect(mapped.filter).toBe(JSON.stringify(filter)) + }) +}) diff --git a/apps/sim/blocks/blocks/notion.ts b/apps/sim/blocks/blocks/notion.ts index 0a36bb70489..39744ee29c6 100644 --- a/apps/sim/blocks/blocks/notion.ts +++ b/apps/sim/blocks/blocks/notion.ts @@ -3,7 +3,7 @@ import { toError } from '@sim/utils/errors' import { NotionIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { createVersionedToolSelector } from '@/blocks/utils' +import { createVersionedToolSelector, parseOptionalJsonInput } from '@/blocks/utils' import { getTrigger } from '@/triggers' /** @@ -622,10 +622,10 @@ export const NotionBlock: BlockConfig = { return 'notion_read' } }, - params: (params) => { + params: (params, context) => { const { oauthCredential, - operation, + operation: storedOperation, properties, filter, sorts, @@ -637,6 +637,7 @@ export const NotionBlock: BlockConfig = { commentContent, ...rest } = params + const operation = storedOperation ?? context?.operation // Parse properties from JSON string for create/add operations let parsedProperties @@ -658,25 +659,9 @@ export const NotionBlock: BlockConfig = { } } - // Parse filter for query database operations - let parsedFilter - if (operation === 'notion_query_database' && filter) { - try { - parsedFilter = JSON.parse(filter) - } catch (error) { - throw new Error(`Invalid JSON for filter: ${toError(error).message}`) - } - } - - // Parse sorts for query database operations - let parsedSorts - if (operation === 'notion_query_database' && sorts) { - try { - parsedSorts = JSON.parse(sorts) - } catch (error) { - throw new Error(`Invalid JSON for sorts: ${toError(error).message}`) - } - } + const isQuery = operation === 'notion_query_database' + const parsedFilter = isQuery ? parseOptionalJsonInput(filter, 'filter') : undefined + const parsedSorts = isQuery ? parseOptionalJsonInput(sorts, 'sorts') : undefined // Parse block children array for append operations let parsedChildren @@ -1028,6 +1013,11 @@ export const NotionV2Block: BlockConfig = { description: 'Page content in markdown format, or comment text for create comment', condition: { field: 'operation', value: ['notion_read', 'notion_create_comment'] }, }, + truncated: { + type: 'boolean', + description: 'True when the page has more than 1000 top-level blocks and content stops there', + condition: { field: 'operation', value: 'notion_read' }, + }, title: { type: 'string', description: 'Page or database title', diff --git a/apps/sim/blocks/blocks/oracle_fusion_financials.ts b/apps/sim/blocks/blocks/oracle_fusion_financials.ts index 87459a427c6..e10bab2e9b0 100644 --- a/apps/sim/blocks/blocks/oracle_fusion_financials.ts +++ b/apps/sim/blocks/blocks/oracle_fusion_financials.ts @@ -18,7 +18,7 @@ export const OracleFusionFinancialsBlock: BlockConfig = { category: 'tools', integrationType: IntegrationType.Commerce, authMode: AuthMode.OAuth, - bgColor: '#C74634', + bgColor: '#FFFFFF', icon: OracleIcon, canvasPresentation: { defaultTitle: 'Oracle Fusion Financials', diff --git a/apps/sim/blocks/blocks/pipedrive.test.ts b/apps/sim/blocks/blocks/pipedrive.test.ts new file mode 100644 index 00000000000..358a27d005b --- /dev/null +++ b/apps/sim/blocks/blocks/pipedrive.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { PipedriveBlock } from '@/blocks/blocks/pipedrive' + +describe('Pipedrive deal status defaults', () => { + const status = PipedriveBlock.subBlocks.find( + (subBlock) => + subBlock.id === 'status' && + typeof subBlock.options === 'function' && + subBlock.condition !== undefined && + JSON.stringify(subBlock.condition).includes('update_deal') + ) + + it('leads with No change everywhere except a known Create Deal', () => { + const options = status?.options + if (typeof options !== 'function') throw new Error('deal status options are static') + expect(options({ values: {} })[0]?.id).toBe('') + expect(options({ values: { operation: 'update_deal' } })[0]?.id).toBe('') + expect(options({ values: { operation: 'create_deal' } })[0]?.id).toBe('open') + expect(options()[0]?.id).toBe('open') + }) +}) diff --git a/apps/sim/blocks/blocks/pipedrive.ts b/apps/sim/blocks/blocks/pipedrive.ts index e75eaafb28e..a0ad699a01f 100644 --- a/apps/sim/blocks/blocks/pipedrive.ts +++ b/apps/sim/blocks/blocks/pipedrive.ts @@ -282,12 +282,22 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`, id: 'status', title: 'Status', type: 'dropdown', - options: [ + dependsOn: ['operation'], + /** + * Every context except a known Create Deal leads with "No change" so an update does not + * reopen deals; that includes agent tool rows, whose values carry no operation. `dependsOn` + * clears the value on an operation switch so it reseeds. Without block values (Copilot + * validation, catalogs) every option is listed, with "No change" last so the default stays Open. + */ + options: (params) => [ + ...(params && params.values.operation !== 'create_deal' + ? [{ label: 'No change', id: '' }] + : []), { label: 'Open', id: 'open' }, { label: 'Won', id: 'won' }, { label: 'Lost', id: 'lost' }, + ...(params ? [] : [{ label: 'No change', id: '' }]), ], - value: () => 'open', condition: { field: 'operation', value: ['create_deal', 'update_deal'] }, }, { @@ -399,7 +409,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n placeholder: 'Pagination cursor from previous response', condition: { field: 'operation', - value: ['get_all_deals', 'get_projects'], + value: ['get_all_deals', 'get_projects', 'get_pipelines'], }, }, { @@ -415,7 +425,6 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n 'get_files', 'get_pipeline_deals', 'get_mail_messages', - 'get_pipelines', ], }, }, @@ -448,6 +457,8 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n { label: 'All', id: '' }, { label: 'Open', id: 'open' }, { label: 'Completed', id: 'completed' }, + { label: 'Canceled', id: 'canceled' }, + { label: 'Deleted', id: 'deleted' }, ], value: () => '', condition: { field: 'operation', value: ['get_projects'] }, @@ -580,7 +591,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n title: 'Subject', type: 'short-input', placeholder: 'Activity subject/title', - required: true, + required: { field: 'operation', value: 'create_activity' }, condition: { field: 'operation', value: ['create_activity', 'update_activity'] }, }, { @@ -604,7 +615,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n title: 'Due Date', type: 'short-input', placeholder: 'YYYY-MM-DD', - required: true, + required: { field: 'operation', value: 'create_activity' }, condition: { field: 'operation', value: ['create_activity', 'update_activity'] }, wandConfig: { enabled: true, @@ -666,10 +677,11 @@ Return ONLY the time string in HH:MM format - no explanations, no quotes, no ext title: 'Mark as Done', type: 'dropdown', options: [ + { label: 'No change', id: '' }, { label: 'Not Done', id: '0' }, { label: 'Done', id: '1' }, ], - value: () => '0', + value: () => '', condition: { field: 'operation', value: ['update_activity'] }, }, { @@ -764,10 +776,11 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n title: 'Archive Lead', type: 'dropdown', options: [ + { label: 'No change', id: '' }, { label: 'No', id: 'false' }, { label: 'Yes', id: 'true' }, ], - value: () => 'false', + value: () => '', condition: { field: 'operation', value: ['update_lead'] }, }, { diff --git a/apps/sim/blocks/blocks/quickbooks.ts b/apps/sim/blocks/blocks/quickbooks.ts index 37c36420f17..3c6fc46500a 100644 --- a/apps/sim/blocks/blocks/quickbooks.ts +++ b/apps/sim/blocks/blocks/quickbooks.ts @@ -2,7 +2,7 @@ import { QuickBooksIcon } from '@/components/icons' import { getScopesForService } from '@/lib/oauth/utils' import type { BlockConfig, BlockMeta, OutputCondition, SubBlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, toolCallArg } from '@/blocks/utils' import { getQuickBooksReportTypesSupporting, type QuickBooksReportControl, @@ -2525,25 +2525,35 @@ export const QuickBooksBlock: BlockConfig = { } return operation }, - params: (params) => { - const operation = String(params.operation) + params: (params, context) => { + const operation = String(params.operation ?? context?.operation) const oauthCredentialValue = params.oauthCredential + /** + * Resolves a tool param filled by a differently-named subBlock. On an agent tool call + * the model's argument arrives under the tool param name, so it is the fallback when + * the dedicated subBlock is empty instead of being overwritten with `undefined`. + */ + const pick = (dedicated: unknown, toolParam: string): unknown => + optionalValue(dedicated) ?? optionalValue(toolCallArg(context, params[toolParam])) + /** {@link pick} for a dropdown whose `'default'` option means "omit". */ + const pickChoice = (dedicated: unknown, toolParam: string): unknown => + pick(dedicated === 'default' ? undefined : dedicated, toolParam) if (operation === EMAIL_TRANSACTION_OPERATION) { return { credential: oauthCredentialValue, - transactionType: params.documentTransactionType, - transactionId: optionalValue(params.documentTransactionId), - recipient: optionalValue(params.recipientOverride), + transactionType: pick(params.documentTransactionType, 'transactionType'), + transactionId: pick(params.documentTransactionId, 'transactionId'), + recipient: pick(params.recipientOverride, 'recipient'), confirmSend: parseConfirmation(params.confirmSend, 'confirmSend'), } } if (operation === DOWNLOAD_TRANSACTION_PDF_OPERATION) { return { credential: oauthCredentialValue, - transactionType: params.documentTransactionType, - transactionId: optionalValue(params.documentTransactionId), - fileName: optionalValue(params.documentFileName), + transactionType: pick(params.documentTransactionType, 'transactionType'), + transactionId: pick(params.documentTransactionId, 'transactionId'), + fileName: pick(params.documentFileName, 'fileName'), } } if (operation === READ_ATTACHMENTS_OPERATION) { @@ -2557,8 +2567,8 @@ export const QuickBooksBlock: BlockConfig = { return { credential: oauthCredentialValue, readMode: 'list', - targetType: params.attachmentTargetType, - targetId: optionalValue(params.attachmentTargetId), + targetType: pick(params.attachmentTargetType, 'targetType'), + targetId: pick(params.attachmentTargetId, 'targetId'), startPosition: parsePaginationInteger(params.startPosition, 'startPosition', 1), maxResults: parsePaginationInteger(params.maxResults, 'maxResults', 25), } @@ -2568,26 +2578,31 @@ export const QuickBooksBlock: BlockConfig = { return { credential: oauthCredentialValue, attachmentKind, - targetType: params.attachmentTargetType, - targetId: optionalValue(params.attachmentTargetId), + targetType: pick(params.attachmentTargetType, 'targetType'), + targetId: pick(params.attachmentTargetId, 'targetId'), file: attachmentKind === 'file' - ? normalizeFileInput(params.attachmentFile, { single: true }) + ? (normalizeFileInput(params.attachmentFile, { single: true }) ?? + normalizeFileInput(toolCallArg(context, params.file), { single: true })) : undefined, fileName: - attachmentKind === 'file' ? optionalValue(params.attachmentFileName) : undefined, + attachmentKind === 'file' ? pick(params.attachmentFileName, 'fileName') : undefined, contentType: - attachmentKind === 'file' ? optionalValue(params.attachmentContentType) : undefined, + attachmentKind === 'file' + ? pick(params.attachmentContentType, 'contentType') + : undefined, description: - attachmentKind === 'file' ? optionalValue(params.attachmentDescription) : undefined, - note: attachmentKind === 'note' ? optionalValue(params.attachmentNote) : undefined, + attachmentKind === 'file' + ? pick(params.attachmentDescription, 'description') + : undefined, + note: attachmentKind === 'note' ? pick(params.attachmentNote, 'note') : undefined, } } if (operation === DOWNLOAD_ATTACHMENT_OPERATION) { return { credential: oauthCredentialValue, attachmentId: optionalValue(params.attachmentId), - fileName: optionalValue(params.downloadAttachmentFileName), + fileName: pick(params.downloadAttachmentFileName, 'fileName'), } } @@ -2604,7 +2619,7 @@ export const QuickBooksBlock: BlockConfig = { credential: oauthCredentialValue, recordType: params.recordType, readMode: params.readMode, - activeStatus: params.readActiveStatus ?? 'default', + activeStatus: pickChoice(params.readActiveStatus, 'activeStatus') ?? 'default', startPosition: parsePaginationInteger(params.startPosition, 'startPosition', 1), maxResults: parsePaginationInteger(params.maxResults, 'maxResults', 25), } @@ -2615,16 +2630,16 @@ export const QuickBooksBlock: BlockConfig = { credential: oauthCredentialValue, transactionType: params.transactionType, readMode: params.readMode, - transactionId: optionalValue(params.readTransactionId), + transactionId: pick(params.readTransactionId, 'transactionId'), } } return { credential: oauthCredentialValue, transactionType: params.transactionType, readMode: params.readMode, - startDate: optionalValue(params.readStartDate), - endDate: optionalValue(params.readEndDate), - customerId: optionalValue(params.readCustomerId), + startDate: pick(params.readStartDate, 'startDate'), + endDate: pick(params.readEndDate, 'endDate'), + customerId: pick(params.readCustomerId, 'customerId'), startPosition: parsePaginationInteger(params.startPosition, 'startPosition', 1), maxResults: parsePaginationInteger(params.maxResults, 'maxResults', 25), } @@ -2633,21 +2648,21 @@ export const QuickBooksBlock: BlockConfig = { if (params.readMode === 'by_id') { return { credential: oauthCredentialValue, - transactionType: params.purchasingTransactionType, + transactionType: pick(params.purchasingTransactionType, 'transactionType'), readMode: params.readMode, - transactionId: optionalValue(params.readTransactionId), + transactionId: pick(params.readTransactionId, 'transactionId'), } } return { credential: oauthCredentialValue, - transactionType: params.purchasingTransactionType, + transactionType: pick(params.purchasingTransactionType, 'transactionType'), readMode: params.readMode, - startDate: optionalValue(params.readStartDate), - endDate: optionalValue(params.readEndDate), + startDate: pick(params.readStartDate, 'startDate'), + endDate: pick(params.readEndDate, 'endDate'), vendorId: - params.purchasingTransactionType === 'purchase' + pick(params.purchasingTransactionType, 'transactionType') === 'purchase' ? undefined - : optionalValue(params.readVendorId), + : pick(params.readVendorId, 'vendorId'), startPosition: parsePaginationInteger(params.startPosition, 'startPosition', 1), maxResults: parsePaginationInteger(params.maxResults, 'maxResults', 25), } @@ -2656,98 +2671,116 @@ export const QuickBooksBlock: BlockConfig = { if (params.readMode === 'by_id') { return { credential: oauthCredentialValue, - transactionType: params.accountingTransactionType, + transactionType: pick(params.accountingTransactionType, 'transactionType'), readMode: params.readMode, - transactionId: optionalValue(params.readTransactionId), + transactionId: pick(params.readTransactionId, 'transactionId'), } } return { credential: oauthCredentialValue, - transactionType: params.accountingTransactionType, + transactionType: pick(params.accountingTransactionType, 'transactionType'), readMode: params.readMode, - startDate: optionalValue(params.readStartDate), - endDate: optionalValue(params.readEndDate), + startDate: pick(params.readStartDate, 'startDate'), + endDate: pick(params.readEndDate, 'endDate'), startPosition: parsePaginationInteger(params.startPosition, 'startPosition', 1), maxResults: parsePaginationInteger(params.maxResults, 'maxResults', 25), } } if (operation === REPORT_OPERATION) { const reportType = params.reportType + const reportTransactionType = pickChoice(params.reportTransactionType, 'transactionType') + const reportGroupBy = pickChoice(params.reportGroupBy, 'groupBy') + const reportAccountsPayablePaid = pickChoice( + params.reportAccountsPayablePaid, + 'accountsPayablePaid' + ) + const reportAccountsReceivablePaid = pickChoice( + params.reportAccountsReceivablePaid, + 'accountsReceivablePaid' + ) + const reportClearedStatus = pickChoice(params.reportClearedStatus, 'clearedStatus') + const reportSourceAccountType = pickChoice( + params.reportSourceAccountType, + 'sourceAccountType' + ) return { credential: oauthCredentialValue, reportType, startDate: reportSupports(reportType, 'startDate') - ? optionalValue(params.reportStartDate) + ? pick(params.reportStartDate, 'startDate') : undefined, - endDate: optionalValue(params.reportEndDate), + endDate: pick(params.reportEndDate, 'endDate'), dateMacro: reportSupports(reportType, 'dateMacro') - ? (params.reportDateMacro ?? 'default') + ? (pickChoice(params.reportDateMacro, 'dateMacro') ?? 'default') : undefined, accountingMethod: reportSupports(reportType, 'accountingMethod') - ? (params.reportAccountingMethod ?? 'default') + ? (pickChoice(params.reportAccountingMethod, 'accountingMethod') ?? 'default') : undefined, summarizeBy: reportSupports(reportType, 'summarizeBy') - ? (params.reportSummarizeBy ?? 'default') + ? (pickChoice(params.reportSummarizeBy, 'summarizeBy') ?? 'default') : undefined, quickZoomUrl: reportSupports(reportType, 'quickZoomUrl') - ? parseOptionalBoolean(params.reportQuickZoomUrl, 'quickZoomUrl') + ? parseOptionalBoolean( + pick(params.reportQuickZoomUrl, 'quickZoomUrl'), + 'quickZoomUrl' + ) : undefined, customerId: reportSupports(reportType, 'customerId') - ? optionalValue(params.reportCustomerId) + ? pick(params.reportCustomerId, 'customerId') : undefined, vendorId: reportSupports(reportType, 'vendorId') - ? optionalValue(params.reportVendorId) + ? pick(params.reportVendorId, 'vendorId') : undefined, accountId: reportSupports(reportType, 'accountId') - ? optionalValue(params.reportAccountId) + ? pick(params.reportAccountId, 'accountId') : undefined, employeeId: reportSupports(reportType, 'employeeId') - ? optionalValue(params.reportEmployeeId) + ? pick(params.reportEmployeeId, 'employeeId') : undefined, itemId: reportSupports(reportType, 'itemId') - ? optionalValue(params.reportItemId) + ? pick(params.reportItemId, 'itemId') : undefined, classId: reportSupports(reportType, 'classId') - ? optionalValue(params.reportClassId) + ? pick(params.reportClassId, 'classId') : undefined, departmentId: reportSupports(reportType, 'departmentId') - ? optionalValue(params.reportDepartmentId) + ? pick(params.reportDepartmentId, 'departmentId') : undefined, agingMethod: reportSupports(reportType, 'agingMethod') - ? (params.reportAgingMethod ?? 'default') + ? (pickChoice(params.reportAgingMethod, 'agingMethod') ?? 'default') : undefined, agingDays: reportSupports(reportType, 'agingPeriod') - ? parseOptionalPositiveInteger(params.reportAgingDays, 'agingDays') + ? parseOptionalPositiveInteger(pick(params.reportAgingDays, 'agingDays'), 'agingDays') : undefined, transactionType: - reportType === 'transaction_list' && params.reportTransactionType !== 'default' - ? params.reportTransactionType + reportType === 'transaction_list' && reportTransactionType !== 'default' + ? reportTransactionType : undefined, groupBy: - reportSupports(reportType, 'groupBy') && params.reportGroupBy !== 'default' - ? params.reportGroupBy + reportSupports(reportType, 'groupBy') && reportGroupBy !== 'default' + ? reportGroupBy : undefined, accountsPayablePaid: reportSupports(reportType, 'accountsPayablePaid') && - params.reportAccountsPayablePaid !== 'default' - ? params.reportAccountsPayablePaid + reportAccountsPayablePaid !== 'default' + ? reportAccountsPayablePaid : undefined, accountsReceivablePaid: reportSupports(reportType, 'accountsReceivablePaid') && - params.reportAccountsReceivablePaid !== 'default' - ? params.reportAccountsReceivablePaid + reportAccountsReceivablePaid !== 'default' + ? reportAccountsReceivablePaid : undefined, clearedStatus: - reportType === 'transaction_list' && params.reportClearedStatus !== 'default' - ? params.reportClearedStatus + reportType === 'transaction_list' && reportClearedStatus !== 'default' + ? reportClearedStatus : undefined, documentNumber: reportType === 'transaction_list' - ? optionalValue(params.reportDocumentNumber) + ? pick(params.reportDocumentNumber, 'documentNumber') : undefined, sourceAccountType: - reportType === 'transaction_list' && params.reportSourceAccountType !== 'default' - ? params.reportSourceAccountType + reportType === 'transaction_list' && reportSourceAccountType !== 'default' + ? reportSourceAccountType : undefined, } } @@ -2800,7 +2833,7 @@ export const QuickBooksBlock: BlockConfig = { const isCreate = operation === 'quickbooks_create_customer_payment' return { credential: oauthCredentialValue, - paymentId: isCreate ? undefined : optionalValue(params.transactionId), + paymentId: isCreate ? undefined : pick(params.transactionId, 'paymentId'), syncToken: isCreate ? undefined : optionalValue(params.syncToken), customerId: optionalValue(params.customerId), totalAmount: parseOptionalNumber(params.totalAmount, 'totalAmount'), @@ -2843,13 +2876,18 @@ export const QuickBooksBlock: BlockConfig = { return { credential: oauthCredentialValue, purchaseOrderId: - !isCreate && isPurchaseOrder ? optionalValue(params.transactionId) : undefined, - billId: !isCreate && isBill ? optionalValue(params.transactionId) : undefined, + !isCreate && isPurchaseOrder + ? pick(params.transactionId, 'purchaseOrderId') + : undefined, + billId: !isCreate && isBill ? pick(params.transactionId, 'billId') : undefined, billPaymentId: - !isCreate && isBillPayment ? optionalValue(params.transactionId) : undefined, + !isCreate && isBillPayment ? pick(params.transactionId, 'billPaymentId') : undefined, vendorCreditId: - !isCreate && isVendorCredit ? optionalValue(params.transactionId) : undefined, - purchaseId: !isCreate && isPurchase ? optionalValue(params.transactionId) : undefined, + !isCreate && isVendorCredit + ? pick(params.transactionId, 'vendorCreditId') + : undefined, + purchaseId: + !isCreate && isPurchase ? pick(params.transactionId, 'purchaseId') : undefined, syncToken: isCreate ? undefined : optionalValue(params.syncToken), vendorId: optionalValue(params.vendorId), apAccountId: @@ -2858,7 +2896,7 @@ export const QuickBooksBlock: BlockConfig = { : undefined, lines: isCreate && (isPurchaseOrder || isBill || isVendorCredit || isPurchase) - ? parseJsonArrayInput(params.purchasingLines, 'lines') + ? parseJsonArrayInput(pick(params.purchasingLines, 'lines'), 'lines') : undefined, totalAmount: isCreate && isBillPayment @@ -2866,9 +2904,9 @@ export const QuickBooksBlock: BlockConfig = { : undefined, paymentType: isCreate && isBillPayment - ? optionalValue(params.billPaymentType) + ? pick(params.billPaymentType, 'paymentType') : isCreate && isPurchase - ? optionalValue(params.purchasePaymentType) + ? pick(params.purchasePaymentType, 'paymentType') : undefined, paymentAccountId: isCreate && (isBillPayment || isPurchase) @@ -2908,15 +2946,17 @@ export const QuickBooksBlock: BlockConfig = { return { credential: oauthCredentialValue, journalEntryId: - !isCreate && isJournalEntry ? optionalValue(params.transactionId) : undefined, + !isCreate && isJournalEntry + ? pick(params.transactionId, 'journalEntryId') + : undefined, depositId: - !isCreate && !isJournalEntry ? optionalValue(params.transactionId) : undefined, + !isCreate && !isJournalEntry ? pick(params.transactionId, 'depositId') : undefined, syncToken: isCreate ? undefined : optionalValue(params.syncToken), lines: isCreate && isJournalEntry - ? parseJsonArrayInput(params.journalLines, 'lines') + ? parseJsonArrayInput(pick(params.journalLines, 'lines'), 'lines') : isCreate - ? parseJsonArrayInput(params.depositLines, 'lines') + ? parseJsonArrayInput(pick(params.depositLines, 'lines'), 'lines') : undefined, confirmPosting: isJournalEntry ? parseConfirmation(params.confirmPosting, 'confirmPosting') diff --git a/apps/sim/blocks/blocks/sap_concur.test.ts b/apps/sim/blocks/blocks/sap_concur.test.ts new file mode 100644 index 00000000000..e95b1196ba5 --- /dev/null +++ b/apps/sim/blocks/blocks/sap_concur.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest' +import { SapConcurBlock } from '@/blocks/blocks/sap_concur' + +const operation = 'sap_concur_list_travel_requests' + +function mapParams(params: Record, context?: { operation: string }) { + const mapper = SapConcurBlock.tools.config?.params + if (!mapper) throw new Error('SAP Concur block has no params mapper') + return mapper({ operation, ...params }, context) +} + +describe('SAP Concur list travel requests mapping', () => { + it('keeps the model user and sort arguments on a tool call', () => { + const mapped = mapParams( + { userId: 'user-1', sortField: 'startDate', sortOrder: 'DESC' }, + { operation } + ) + + expect(mapped.userId).toBe('user-1') + expect(mapped.sortField).toBe('startDate') + expect(mapped.sortOrder).toBe('DESC') + }) + + it('prefers the configured subBlock over the model argument', () => { + const mapped = mapParams({ travelRequestUserId: 'configured', userId: 'model' }, { operation }) + + expect(mapped.userId).toBe('configured') + }) + + it('does not forward a stale same-named value on the canvas', () => { + const mapped = mapParams({ userId: 'stale-from-another-operation' }) + + expect(mapped.userId).toBeUndefined() + }) +}) + +describe('SAP Concur agent operation resolution', () => { + it('maps a receipt upload when the operation arrives only in the tool-call context', () => { + const mapper = SapConcurBlock.tools.config?.params + if (!mapper) throw new Error('SAP Concur block has no params mapper') + const receipt = { id: 'file-1', name: 'receipt.png', url: 'https://example.com/receipt.png' } + + const mapped = mapper( + { clientId: 'id', clientSecret: 'secret', userId: 'user-1', receipt }, + { operation: 'sap_concur_upload_receipt_image' } + ) + + expect(mapped.receipt).toMatchObject({ id: 'file-1' }) + expect(mapped.userId).toBe('user-1') + }) +}) diff --git a/apps/sim/blocks/blocks/sap_concur.ts b/apps/sim/blocks/blocks/sap_concur.ts index 0d78f1c4c9a..e0d2443c65e 100644 --- a/apps/sim/blocks/blocks/sap_concur.ts +++ b/apps/sim/blocks/blocks/sap_concur.ts @@ -1,7 +1,7 @@ import { SapConcurIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput } from '@/blocks/utils' +import { normalizeFileInput, toolCallArg } from '@/blocks/utils' import type { UserFileLike } from '@/tools/sap_concur/types' const toBool = (v: unknown): boolean | undefined => { @@ -2029,7 +2029,8 @@ Return ONLY the JSON object - no explanations, no extra text.`, ], config: { tool: (params) => params.operation, - params: (params) => { + params: (params, context) => { + const operation = params.operation ?? context?.operation const auth = { datacenter: params.datacenter || undefined, grantType: params.grantType || undefined, @@ -2045,13 +2046,13 @@ Return ONLY the JSON object - no explanations, no extra text.`, const start = params.start ? Number(params.start) : undefined const count = params.count ? Number(params.count) : undefined const page = params.page ? Number(params.page) : undefined - const contextType = clampContextType(params.operation, params.contextType) + const contextType = clampContextType(operation, params.contextType) - switch (params.operation) { + switch (operation) { case 'sap_concur_list_expense_reports': return { ...auth, - user: params.expenseReportUser || undefined, + user: params.expenseReportUser || toolCallArg(context, params.user), submitDateBefore: params.submitDateBefore || undefined, submitDateAfter: params.submitDateAfter || undefined, paidDateBefore: params.paidDateBefore || undefined, @@ -2116,8 +2117,8 @@ Return ONLY the JSON object - no explanations, no extra text.`, ...auth, userId: params.userId, contextType: 'MANAGER', - sort: params.reportsToApproveSort || undefined, - order: params.reportsToApproveOrder || undefined, + sort: params.reportsToApproveSort || toolCallArg(context, params.sort), + order: params.reportsToApproveOrder || toolCallArg(context, params.order), includeDelegateApprovals: toBool(params.includeDelegateApprovals), } case 'sap_concur_list_expenses': @@ -2241,32 +2242,40 @@ Return ONLY the JSON object - no explanations, no extra text.`, view: params.view || undefined, limit, start, - userId: params.travelRequestUserId || undefined, - approvedBefore: params.travelRequestApprovedBefore || undefined, - approvedAfter: params.travelRequestApprovedAfter || undefined, - modifiedBefore: params.travelRequestModifiedBefore || undefined, - modifiedAfter: params.travelRequestModifiedAfter || undefined, - sortField: params.travelRequestSortField || undefined, + userId: params.travelRequestUserId || toolCallArg(context, params.userId), + approvedBefore: + params.travelRequestApprovedBefore || toolCallArg(context, params.approvedBefore), + approvedAfter: + params.travelRequestApprovedAfter || toolCallArg(context, params.approvedAfter), + modifiedBefore: + params.travelRequestModifiedBefore || toolCallArg(context, params.modifiedBefore), + modifiedAfter: + params.travelRequestModifiedAfter || toolCallArg(context, params.modifiedAfter), + sortField: params.travelRequestSortField || toolCallArg(context, params.sortField), sortOrder: params.travelRequestSortOrder === 'asc' || params.travelRequestSortOrder === 'desc' ? params.travelRequestSortOrder - : undefined, + : toolCallArg(context, params.sortOrder), } case 'sap_concur_get_travel_request': case 'sap_concur_delete_travel_request': return { ...auth, requestUuid: params.requestUuid, - userId: params.travelRequestUserId || undefined, + userId: params.travelRequestUserId || toolCallArg(context, params.userId), } case 'sap_concur_create_travel_request': - return { ...auth, body: params.body, userId: params.travelRequestUserId || undefined } + return { + ...auth, + body: params.body, + userId: params.travelRequestUserId || toolCallArg(context, params.userId), + } case 'sap_concur_update_travel_request': return { ...auth, requestUuid: params.requestUuid, body: params.body, - userId: params.travelRequestUserId || undefined, + userId: params.travelRequestUserId || toolCallArg(context, params.userId), } case 'sap_concur_move_travel_request': return { @@ -2274,10 +2283,12 @@ Return ONLY the JSON object - no explanations, no extra text.`, requestUuid: params.requestUuid, action: params.action, body: params.body || undefined, - userId: params.travelRequestUserId || undefined, + userId: params.travelRequestUserId || toolCallArg(context, params.userId), companyID: params.companyID || undefined, comment: - params.action === 'sendback' ? params.sendbackComment || undefined : undefined, + params.action === 'sendback' + ? params.sendbackComment || toolCallArg(context, params.comment) + : undefined, } case 'sap_concur_list_travel_request_comments': return { ...auth, requestUuid: params.requestUuid } @@ -2285,7 +2296,7 @@ Return ONLY the JSON object - no explanations, no extra text.`, return { ...auth, requestUuid: params.requestUuid, - userId: params.expectedExpenseUserId || undefined, + userId: params.expectedExpenseUserId || toolCallArg(context, params.userId), } case 'sap_concur_get_request_cash_advance': return { ...auth, cashAdvanceUuid: params.cashAdvanceUuid } @@ -2294,21 +2305,21 @@ Return ONLY the JSON object - no explanations, no extra text.`, ...auth, requestUuid: params.requestUuid, body: params.body, - userId: params.expectedExpenseUserId || undefined, + userId: params.expectedExpenseUserId || toolCallArg(context, params.userId), } case 'sap_concur_get_expected_expense': case 'sap_concur_delete_expected_expense': return { ...auth, expenseUuid: params.expenseUuid, - userId: params.expectedExpenseUserId || undefined, + userId: params.expectedExpenseUserId || toolCallArg(context, params.userId), } case 'sap_concur_update_expected_expense': return { ...auth, expenseUuid: params.expenseUuid, body: params.body, - userId: params.expectedExpenseUserId || undefined, + userId: params.expectedExpenseUserId || toolCallArg(context, params.userId), } case 'sap_concur_create_cash_advance': return { ...auth, body: params.body } @@ -2330,15 +2341,21 @@ Return ONLY the JSON object - no explanations, no extra text.`, useridValue: params.useridValue || undefined, itemsPerPage: params.itineraryItemsPerPage ? Number(params.itineraryItemsPerPage) - : undefined, - page: params.itineraryPage ? Number(params.itineraryPage) : undefined, + : toolCallArg( + context, + params.itemsPerPage ? Number(params.itemsPerPage) : undefined + ), + page: params.itineraryPage + ? Number(params.itineraryPage) + : toolCallArg(context, page), includeMetadata: toBool(params.includeMetadata), includeCanceledTrips: toBool(params.includeCanceledTrips), includeVirtualTrip: params.includeVirtualTrip || undefined, includeGuestBookings: toBool(params.includeGuestBookings), createdAfterDate: params.createdAfterDate || undefined, createdBeforeDate: params.createdBeforeDate || undefined, - lastModifiedDate: params.itineraryLastModifiedDate || undefined, + lastModifiedDate: + params.itineraryLastModifiedDate || toolCallArg(context, params.lastModifiedDate), } case 'sap_concur_get_itinerary': return { @@ -2352,7 +2369,7 @@ Return ONLY the JSON object - no explanations, no extra text.`, return { ...auth, count, - cursor: params.usersCursor || undefined, + cursor: params.usersCursor || toolCallArg(context, params.cursor), attributes: params.attributes || undefined, excludedAttributes: params.excludedAttributes || undefined, } @@ -2375,7 +2392,7 @@ Return ONLY the JSON object - no explanations, no extra text.`, return { ...auth, page, - sortBy: clampSortBy(params.operation, params.sortBy), + sortBy: clampSortBy(operation, params.sortBy), sortDirection: params.sortDirection || undefined, value: params.value || undefined, categoryType: params.categoryType || undefined, @@ -2389,7 +2406,7 @@ Return ONLY the JSON object - no explanations, no extra text.`, ...auth, listId: params.listId, page, - sortBy: clampSortBy(params.operation, params.sortBy), + sortBy: clampSortBy(operation, params.sortBy), sortDirection: params.sortDirection || undefined, hasChildren: toBool(params.hasChildren), isDeleted: params.isDeleted || undefined, @@ -2435,7 +2452,9 @@ Return ONLY the JSON object - no explanations, no extra text.`, return { ...auth, lastModifiedDate: params.lastModifiedDate, - page: params.travelProfilePage ? Number(params.travelProfilePage) : undefined, + page: params.travelProfilePage + ? Number(params.travelProfilePage) + : toolCallArg(context, page), itemsPerPage: params.itemsPerPage ? Number(params.itemsPerPage) : undefined, travelConfigs: params.travelConfigs || undefined, active: params.active || undefined, @@ -2474,7 +2493,7 @@ Return ONLY the JSON object - no explanations, no extra text.`, } } default: - throw new Error(`Unsupported SAP Concur operation: ${params.operation}`) + throw new Error(`Unsupported SAP Concur operation: ${operation}`) } }, }, diff --git a/apps/sim/blocks/blocks/sap_s4hana.test.ts b/apps/sim/blocks/blocks/sap_s4hana.test.ts new file mode 100644 index 00000000000..638f1800a23 --- /dev/null +++ b/apps/sim/blocks/blocks/sap_s4hana.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' +import { SapS4HanaBlock } from '@/blocks/blocks/sap_s4hana' +import type { BlockToolParamsContext } from '@/blocks/types' + +const mapParams = ( + inputs: Record, + context?: BlockToolParamsContext +): Record => SapS4HanaBlock.tools.config?.params?.(inputs, context) ?? {} + +const items = [{ Material: 'M-1', RequestedQuantity: '1' }] + +/** On an agent call the stored operation reaches the mapper only on `context`, never in the args. */ +describe('SAP S/4HANA agent tool calls', () => { + it('maps sales order items set on the tool to the items param', () => { + expect( + mapParams({ salesOrderItems: items }, { operation: 'sap_s4hana_create_sales_order' }).items + ).toEqual(items) + }) + + it('keeps the model OData service, path and body when the tool fields are blank', () => { + const mapped = mapParams( + { service: 'API_PRODUCT_SRV', path: '/A_Product', method: 'POST', body: { Product: 'P1' } }, + { operation: 'sap_s4hana_odata_query' } + ) + expect(mapped).toMatchObject({ + service: 'API_PRODUCT_SRV', + path: '/A_Product', + method: 'POST', + body: { Product: 'P1' }, + }) + }) + + it('maps canvas OData fields exactly as before', () => { + const mapped = mapParams({ + operation: 'sap_s4hana_odata_query', + odataService: 'API_PRODUCT_SRV', + odataPath: '/A_Product', + }) + expect(mapped).toMatchObject({ service: 'API_PRODUCT_SRV', path: '/A_Product', method: 'GET' }) + }) +}) diff --git a/apps/sim/blocks/blocks/sap_s4hana.ts b/apps/sim/blocks/blocks/sap_s4hana.ts index ac705937c6f..24c267b5400 100644 --- a/apps/sim/blocks/blocks/sap_s4hana.ts +++ b/apps/sim/blocks/blocks/sap_s4hana.ts @@ -1,6 +1,7 @@ import { SapS4HanaIcon } from '@/components/icons' import type { BlockConfig, BlockMeta, CanvasSentence } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' +import { isToolCall } from '@/blocks/utils' /** * Whichever name a new business partner carries: an organization has @@ -209,12 +210,12 @@ Rules: - Functions: substringof('x', Field), startswith(Field, 'x'), endswith(Field, 'x') Examples: -- BusinessPartnerCategory eq '1' and Country eq 'US' +- BusinessPartnerCategory eq '1' and CorrespondenceLanguage eq 'EN' - CreationDate gt datetime'2024-01-01T00:00:00' - substringof('ACME', OrganizationBPName1) Return ONLY the $filter expression - no explanations, no extra text.`, - placeholder: 'Describe the filter you want (e.g., "people in the US created this year")', + placeholder: 'Describe the filter you want (e.g., "people created this year")', }, condition: { field: 'operation', @@ -1045,7 +1046,15 @@ Return ONLY the JSON array - no explanations, no extra text.`, ], config: { tool: (params) => params.operation, - params: (params) => { + params: (params, context) => { + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const operation = params.operation ?? context?.operation + /** + * A differently named sub-block feeds each tool param below. On a tool call the model's + * arg arrives under the tool param name, so a blank sub-block falls back to it. + */ + const pick = (dedicated: unknown, toolParam: string): unknown => + isToolCall(context) ? dedicated || params[toolParam] : dedicated const auth = { deploymentType: params.deploymentType || 'cloud_public', authType: params.authType || 'oauth_client_credentials', @@ -1060,8 +1069,8 @@ Return ONLY the JSON array - no explanations, no extra text.`, } const listFields = { filter: params.filter || undefined, - top: params.top ? Number(params.top) : undefined, - skip: params.skip ? Number(params.skip) : undefined, + top: params.top != null && params.top !== '' ? Number(params.top) : undefined, + skip: params.skip != null && params.skip !== '' ? Number(params.skip) : undefined, orderBy: params.orderBy || undefined, select: params.select || undefined, expand: params.expand || undefined, @@ -1071,7 +1080,7 @@ Return ONLY the JSON array - no explanations, no extra text.`, expand: params.expand || undefined, } - switch (params.operation) { + switch (operation) { case 'sap_s4hana_list_business_partners': return { ...auth, ...listFields } case 'sap_s4hana_get_business_partner': @@ -1084,14 +1093,14 @@ Return ONLY the JSON array - no explanations, no extra text.`, firstName: params.firstName || undefined, lastName: params.lastName || undefined, organizationBPName1: params.organizationBPName1 || undefined, - body: params.businessPartnerBody || undefined, + body: pick(params.businessPartnerBody, 'body') || undefined, } case 'sap_s4hana_update_business_partner': return { ...auth, businessPartner: params.businessPartner, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_customers': return { ...auth, ...listFields } @@ -1101,8 +1110,8 @@ Return ONLY the JSON array - no explanations, no extra text.`, return { ...auth, customer: params.customer, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_suppliers': return { ...auth, ...listFields } @@ -1112,8 +1121,8 @@ Return ONLY the JSON array - no explanations, no extra text.`, return { ...auth, supplier: params.supplier, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_sales_orders': return { ...auth, ...listFields } @@ -1127,21 +1136,21 @@ Return ONLY the JSON array - no explanations, no extra text.`, distributionChannel: params.distributionChannel, organizationDivision: params.organizationDivision, soldToParty: params.soldToParty, - items: params.salesOrderItems, - body: params.salesOrderBody || undefined, + items: pick(params.salesOrderItems, 'items'), + body: pick(params.salesOrderBody, 'body') || undefined, } case 'sap_s4hana_update_sales_order': return { ...auth, salesOrder: params.salesOrder, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_delete_sales_order': return { ...auth, salesOrder: params.salesOrder, - ifMatch: params.updateIfMatch || undefined, + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_outbound_deliveries': return { ...auth, ...listFields } @@ -1171,8 +1180,8 @@ Return ONLY the JSON array - no explanations, no extra text.`, return { ...auth, product: params.product, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_material_stock': return { ...auth, ...listFields } @@ -1197,15 +1206,15 @@ Return ONLY the JSON array - no explanations, no extra text.`, return { ...auth, purchaseRequisitionType: params.purchaseRequisitionType, - items: params.purchaseRequisitionItems, - body: params.purchaseRequisitionBody || undefined, + items: pick(params.purchaseRequisitionItems, 'items'), + body: pick(params.purchaseRequisitionBody, 'body') || undefined, } case 'sap_s4hana_update_purchase_requisition': return { ...auth, purchaseRequisition: params.purchaseRequisition, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_purchase_orders': return { ...auth, ...listFields } @@ -1219,14 +1228,14 @@ Return ONLY the JSON array - no explanations, no extra text.`, purchasingOrganization: params.purchasingOrganization, purchasingGroup: params.purchasingGroup, supplier: params.supplier, - body: params.purchaseOrderBody || undefined, + body: pick(params.purchaseOrderBody, 'body') || undefined, } case 'sap_s4hana_update_purchase_order': return { ...auth, purchaseOrder: params.purchaseOrder, - body: params.updateBody, - ifMatch: params.updateIfMatch || undefined, + body: pick(params.updateBody, 'body'), + ifMatch: pick(params.updateIfMatch, 'ifMatch') || undefined, } case 'sap_s4hana_list_supplier_invoices': return { ...auth, ...listFields } @@ -1240,12 +1249,12 @@ Return ONLY the JSON array - no explanations, no extra text.`, case 'sap_s4hana_odata_query': return { ...auth, - service: params.odataService, - path: params.odataPath, - method: params.odataMethod || 'GET', - query: params.odataQuery || undefined, - body: params.odataBody || undefined, - ifMatch: params.odataIfMatch || undefined, + service: pick(params.odataService, 'service'), + path: pick(params.odataPath, 'path'), + method: pick(params.odataMethod, 'method') || 'GET', + query: pick(params.odataQuery, 'query') || undefined, + body: pick(params.odataBody, 'body') || undefined, + ifMatch: pick(params.odataIfMatch, 'ifMatch') || undefined, } default: return auth diff --git a/apps/sim/blocks/blocks/servicenow.test.ts b/apps/sim/blocks/blocks/servicenow.test.ts index e882578f21b..b580ac7737b 100644 --- a/apps/sim/blocks/blocks/servicenow.test.ts +++ b/apps/sim/blocks/blocks/servicenow.test.ts @@ -159,3 +159,75 @@ describe('ServiceNow state written after the split is never re-migrated', () => expect(second.blocks['block-1'].subBlocks.readFields?.value).toBe('number,state') }) }) + +/** + * A tool call (agent, Pi, Human v2) passes the model's args under the tool param + * names. Operations whose value has a dedicated, differently-named subblock must + * fall back to the model's arg instead of overwriting it with the blank subblock. + */ +describe('ServiceNow tool-call args reach the tool', () => { + function mapToolCall(operation: string, args: Record): Record { + const transform = ServiceNowBlock.tools.config?.params + if (!transform) throw new Error('ServiceNow block has no params transform') + const params = { ...CREDENTIALS, ...args } + return { ...params, ...transform(params, { operation }) } + } + + it('keeps the model close code and notes on Resolve Incident', () => { + const mapped = mapToolCall('servicenow_resolve_incident', { + sysId: 'abc', + closeCode: 'Solution provided', + closeNotes: 'Restarted the service', + }) + + expect(mapped.closeCode).toBe('Solution provided') + expect(mapped.closeNotes).toBe('Restarted the service') + }) + + it('keeps the model state filter, projection, and display mode on List Incidents', () => { + const mapped = mapToolCall('servicenow_list_incidents', { + state: '2', + fields: 'number,state', + displayValue: 'false', + }) + + expect(mapped.state).toBe('2') + expect(mapped.fields).toBe('number,state') + expect(mapped.displayValue).toBe('false') + }) + + it('does not send a stored Create/Update JSON body as the List Incidents projection', () => { + const mapped = mapToolCall('servicenow_list_incidents', { + fields: '{"short_description":"Printer down"}', + }) + + expect(mapped.fields).toBeUndefined() + }) + + it('keeps the model comment on Update Approval and the search text on Search Knowledge', () => { + expect( + mapToolCall('servicenow_update_approval', { + approvalSysId: 'abc', + decision: 'approved', + comments: 'Looks good', + }).comments + ).toBe('Looks good') + expect(mapToolCall('servicenow_search_knowledge', { query: 'vpn' }).query).toBe('vpn') + }) + + it('keeps the model limit on List Attachments', () => { + const mapped = mapToolCall('servicenow_list_attachments', { + tableName: 'incident', + recordSysId: 'abc', + limit: 5, + }) + + expect(mapped.limit).toBe(5) + }) + + it('prefers a configured subblock value over the model arg', () => { + const mapped = mapToolCall('servicenow_list_incidents', { incidentState: '6', state: '2' }) + + expect(mapped.state).toBe('6') + }) +}) diff --git a/apps/sim/blocks/blocks/servicenow.ts b/apps/sim/blocks/blocks/servicenow.ts index 5724ad3ae06..d1eb197c8e4 100644 --- a/apps/sim/blocks/blocks/servicenow.ts +++ b/apps/sim/blocks/blocks/servicenow.ts @@ -1,7 +1,7 @@ import { ServiceNowIcon } from '@/components/icons' -import type { BlockConfig, BlockMeta } from '@/blocks/types' +import type { BlockConfig, BlockMeta, BlockToolParamsContext } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { normalizeFileInput, withSavedCredential } from '@/blocks/utils' +import { isToolCall, normalizeFileInput, toolCallArg, withSavedCredential } from '@/blocks/utils' import { APPROVAL_DECISION_OPTIONS, APPROVAL_STATE, @@ -1622,9 +1622,9 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st ], config: { tool: (params) => params.operation, - params: (params) => { + params: (params, context?: BlockToolParamsContext) => { const { - operation, + operation: storedOperation, fields, readFields, returnFields, @@ -1644,11 +1644,23 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st knowledgeQuery, ...rest } = params + const operation = context?.operation ?? storedOperation + /** + * A dedicated subblock's value, or on a tool call where it is blank, the + * model's arg under the tool param name. Without a model arg the subblock + * value stands exactly, so an explicit "Any" (`''`) is not replaced. + */ + const dedicatedOrToolArg = (dedicated: unknown, toolArg: unknown) => + isToolCall(context) && + toolArg !== undefined && + (dedicated === undefined || dedicated === null || dedicated === '') + ? toolArg + : dedicated const isCreateOrUpdate = operation === 'servicenow_create_record' || operation === 'servicenow_update_record' if (SEMANTIC_DISPLAY_VALUE_OPS.has(operation)) { - rest.displayValue = semanticDisplayValue + rest.displayValue = dedicatedOrToolArg(semanticDisplayValue, rest.displayValue) } /** @@ -1659,7 +1671,9 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st * query — so each gets its own subblock and is republished here for the * operations that own it. Assign explicitly rather than conditionally: * `finalInputs` merges this result over the raw inputs, so a key left - * off is not dropped, it keeps whatever the raw input held. + * off is not dropped, it keeps whatever the raw input held. On a tool + * call the model supplies these under the tool param names, so a blank + * dedicated subblock falls back to the model's value instead of wiping it. */ const stateSource = operation === 'servicenow_update_change_state' @@ -1669,12 +1683,24 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st : CHANGE_STATE_OPS.has(operation) ? changeState : incidentState - rest.state = SEMANTIC_STATE_OPS.has(operation) ? stateSource : undefined - rest.comments = - operation === 'servicenow_update_approval' ? approvalComments : incidentComments - rest.closeCode = CHANGE_CLOSE_OPS.has(operation) ? changeCloseCode : resolutionCode - rest.closeNotes = CHANGE_CLOSE_OPS.has(operation) ? changeCloseNotes : resolutionNotes - if (operation === 'servicenow_search_knowledge') rest.query = knowledgeQuery + rest.state = SEMANTIC_STATE_OPS.has(operation) + ? dedicatedOrToolArg(stateSource, rest.state) + : undefined + rest.comments = dedicatedOrToolArg( + operation === 'servicenow_update_approval' ? approvalComments : incidentComments, + rest.comments + ) + rest.closeCode = dedicatedOrToolArg( + CHANGE_CLOSE_OPS.has(operation) ? changeCloseCode : resolutionCode, + rest.closeCode + ) + rest.closeNotes = dedicatedOrToolArg( + CHANGE_CLOSE_OPS.has(operation) ? changeCloseNotes : resolutionNotes, + rest.closeNotes + ) + if (operation === 'servicenow_search_knowledge') { + rest.query = dedicatedOrToolArg(knowledgeQuery, rest.query) + } /** * `attachmentLimit` exists as a separate subblock precisely so a limit @@ -1684,8 +1710,8 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st * Every paginated operation reads exactly one of the two. */ if (operation === 'servicenow_list_attachments') { - rest.limit = - attachmentLimit != null && attachmentLimit !== '' ? Number(attachmentLimit) : undefined + const limit = dedicatedOrToolArg(attachmentLimit, rest.limit) + rest.limit = limit != null && limit !== '' ? Number(limit) : undefined } else if (rest.limit != null) { rest.limit = rest.limit === '' ? undefined : Number(rest.limit) } @@ -1741,7 +1767,12 @@ Output: {"state": "2", "assigned_to": "john.doe", "work_notes": "Assigned and st } const projection = operation === 'servicenow_read_record' ? readFields : returnFields - return { ...rest, fields: projection || undefined } + const toolFields = toolCallArg(context, fields) + const toolProjection = + typeof toolFields === 'string' && !toolFields.trim().startsWith('{') + ? toolFields + : undefined + return { ...rest, fields: projection || toolProjection || undefined } }, }, }, diff --git a/apps/sim/blocks/blocks/square.ts b/apps/sim/blocks/blocks/square.ts index 4482564f439..29dbed71cff 100644 --- a/apps/sim/blocks/blocks/square.ts +++ b/apps/sim/blocks/blocks/square.ts @@ -926,7 +926,7 @@ export const SquareBlock: BlockConfig = { ...(coercedVersion !== undefined && { version: coercedVersion }), ...(coercedOrderVersion !== undefined && { orderVersion: coercedOrderVersion }), ...(autocomplete !== undefined && { autocomplete: coerceBoolean(autocomplete) }), - ...(includeRelatedObjects !== undefined && { + ...(includeRelatedObjects != null && { includeRelatedObjects: coerceBoolean(includeRelatedObjects), }), } @@ -1005,6 +1005,14 @@ export const SquareBlock: BlockConfig = { invoices: { type: 'json', description: 'Array of invoice objects' }, object: { type: 'json', description: 'Catalog object' }, objects: { type: 'json', description: 'Array of catalog objects' }, + related_objects: { + type: 'json', + description: 'Catalog objects referenced by the retrieved object (Get Catalog Object)', + }, + id_mappings: { + type: 'json', + description: 'Temporary-to-permanent ID mappings [{client_object_id, object_id}] (Upsert)', + }, counts: { type: 'json', description: 'Array of inventory count objects' }, deleted: { type: 'boolean', description: 'Whether the resource was deleted' }, deleted_object_ids: { type: 'json', description: 'IDs of deleted catalog objects' }, diff --git a/apps/sim/blocks/blocks/workday.test.ts b/apps/sim/blocks/blocks/workday.test.ts new file mode 100644 index 00000000000..9d218b12e3c --- /dev/null +++ b/apps/sim/blocks/blocks/workday.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest' +import { WorkdayBlock } from '@/blocks/blocks/workday' +import type { BlockToolParamsContext } from '@/blocks/types' + +const mapParams = ( + inputs: Record, + context?: BlockToolParamsContext +): Record => WorkdayBlock.tools.config?.params?.(inputs, context) ?? {} + +/** On an agent call the stored operation reaches the mapper only on `context`, never in the args. */ +describe('Workday change job mapping', () => { + it('maps a job profile set on the tool when the operation arrives only on the context', () => { + expect( + mapParams({ workerId: '04345', jobProfileId: 'JP-1' }, { operation: 'change_job' }) + .newJobProfileId + ).toBe('JP-1') + }) + + it('keeps the model newJobProfileId when the tool field is blank', () => { + expect( + mapParams({ workerId: '04345', newJobProfileId: 'JP-2' }, { operation: 'change_job' }) + .newJobProfileId + ).toBe('JP-2') + }) + + it('maps the canvas job profile exactly as before', () => { + expect( + mapParams({ operation: 'change_job', workerId: '04345', jobProfileId: 'JP-1' }) + .newJobProfileId + ).toBe('JP-1') + }) +}) diff --git a/apps/sim/blocks/blocks/workday.ts b/apps/sim/blocks/blocks/workday.ts index f9ad3d91589..78b2acd5d66 100644 --- a/apps/sim/blocks/blocks/workday.ts +++ b/apps/sim/blocks/blocks/workday.ts @@ -112,7 +112,7 @@ export const WorkdayBlock: BlockConfig = { id: 'workerId', title: 'Worker ID', type: 'short-input', - placeholder: 'e.g., 3aa5550b7fe348b98d7b5741afc65534', + placeholder: 'Employee ID (e.g., 21001); WID for onboarding', condition: { field: 'operation', value: [ @@ -183,7 +183,7 @@ export const WorkdayBlock: BlockConfig = { id: 'address', title: 'Address', type: 'short-input', - placeholder: '123 Main St, City, State', + placeholder: '123 Main St', condition: { field: 'operation', value: 'create_prehire' }, mode: 'advanced', }, @@ -274,7 +274,7 @@ export const WorkdayBlock: BlockConfig = { placeholder: '{\n "Marital_Status_Reference": {\n "ID": { "attributes": { "wd:type": "Marital_Status_ID" }, "$value": "Married" }\n }\n}', description: - 'Demographic fields supported by Workday Change_Personal_Information (e.g. Date_of_Birth, Gender_Reference, Marital_Status_Reference, Ethnicity_Reference, Citizenship_Status_Reference). Does not update business title or work contact info.', + 'Demographic fields supported by Workday Change_Personal_Information (e.g. Date_of_Birth, Gender_Reference, Marital_Status_Reference, Ethnicity_Reference, Citizenship_Reference). Does not update business title or work contact info.', condition: { field: 'operation', value: 'update_worker' }, required: { field: 'operation', value: 'update_worker' }, wandConfig: { @@ -286,8 +286,8 @@ export const WorkdayBlock: BlockConfig = { - Date_of_Birth: ISO date string (YYYY-MM-DD) - Gender_Reference: { ID: { attributes: { "wd:type": "Gender_Code" }, $value: "Male" | "Female" | ... } } - Marital_Status_Reference: { ID: { attributes: { "wd:type": "Marital_Status_ID" }, $value: "Married" | "Single" | ... } } +- Citizenship_Reference: same reference shape, with the tenant's citizenship status ID - Ethnicity_Reference: { ID: { attributes: { "wd:type": "Ethnicity_ID" }, $value: "..." } } -- Citizenship_Status_Reference: same shape ### NOT SUPPORTED BY THIS OPERATION - Business title (use Change_Job) @@ -296,6 +296,7 @@ export const WorkdayBlock: BlockConfig = { ### RULES - Output ONLY valid JSON starting with { and ending with } - Include only fields that need updating +- Keep keys in the order listed above (Workday rejects out-of-order elements) ### EXAMPLE User: "Mark marital status as Married" @@ -407,9 +408,18 @@ Output: {"Marital_Status_Reference":{"ID":{"attributes":{"wd:type":"Marital_Stat ], config: { tool: (params) => `workday_${params.operation}`, - params: (params) => { - const { operation, orgType, fields, jobProfileId, locationId, supervisoryOrgId, ...rest } = - params + params: (params, context) => { + const { + operation: storedOperation, + orgType, + fields, + jobProfileId, + locationId, + supervisoryOrgId, + ...rest + } = params + /** Agent tool calls carry the stored operation only on `context`, not in `params`. */ + const operation = storedOperation ?? context?.operation if (rest.limit != null && rest.limit !== '') rest.limit = Number(rest.limit) if (rest.offset != null && rest.offset !== '') rest.offset = Number(rest.offset) diff --git a/apps/sim/blocks/blocks/zendesk.ts b/apps/sim/blocks/blocks/zendesk.ts index fe2063ccfac..9e469856d1b 100644 --- a/apps/sim/blocks/blocks/zendesk.ts +++ b/apps/sim/blocks/blocks/zendesk.ts @@ -1,7 +1,7 @@ import { ZendeskIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' -import { withSavedCredential } from '@/blocks/utils' +import { parseOptionalBooleanInput, toolCallArg, withSavedCredential } from '@/blocks/utils' import { getTrigger } from '@/triggers' export const ZendeskBlock: BlockConfig = { @@ -206,7 +206,7 @@ Return ONLY the subject line - no explanations.`, }, condition: { field: 'operation', - value: ['create_ticket', 'update_ticket'], + value: ['create_ticket'], }, wandConfig: { enabled: true, @@ -226,6 +226,32 @@ Return ONLY the description text - no explanations.`, placeholder: 'Describe the issue in detail...', }, }, + { + id: 'comment', + title: 'Comment', + type: 'long-input', + placeholder: 'Comment to add to the ticket', + condition: { + field: 'operation', + value: ['update_ticket'], + }, + }, + { + id: 'commentPublic', + title: 'Comment Visibility', + type: 'dropdown', + options: [ + { label: 'Ticket default', id: '' }, + { label: 'Public reply', id: 'true' }, + { label: 'Internal note', id: 'false' }, + ], + value: () => '', + condition: { + field: 'operation', + value: ['update_ticket'], + }, + mode: 'advanced', + }, { id: 'status', title: 'Status', @@ -233,7 +259,7 @@ Return ONLY the description text - no explanations.`, placeholder: 'Status (new, open, pending, hold, solved, closed)', condition: { field: 'operation', - value: ['get_tickets', 'create_ticket', 'update_ticket'], + value: ['get_tickets', 'create_ticket', 'update_ticket', 'update_tickets_bulk'], }, mode: 'advanced', }, @@ -244,7 +270,7 @@ Return ONLY the description text - no explanations.`, placeholder: 'Priority (low, normal, high, urgent)', condition: { field: 'operation', - value: ['get_tickets', 'create_ticket', 'update_ticket'], + value: ['get_tickets', 'create_ticket', 'update_ticket', 'update_tickets_bulk'], }, mode: 'advanced', }, @@ -266,7 +292,15 @@ Return ONLY the description text - no explanations.`, placeholder: 'Comma-separated tags', condition: { field: 'operation', - value: ['create_ticket', 'update_ticket'], + value: [ + 'create_ticket', + 'update_ticket', + 'update_tickets_bulk', + 'create_user', + 'update_user', + 'create_organization', + 'update_organization', + ], }, mode: 'advanced', }, @@ -277,7 +311,18 @@ Return ONLY the description text - no explanations.`, placeholder: 'User ID to assign ticket to', condition: { field: 'operation', - value: ['get_tickets', 'create_ticket', 'update_ticket'], + value: ['get_tickets', 'create_ticket', 'update_ticket', 'update_tickets_bulk'], + }, + mode: 'advanced', + }, + { + id: 'requesterId', + title: 'Requester ID', + type: 'short-input', + placeholder: 'User ID of the requester', + condition: { + field: 'operation', + value: ['create_ticket'], }, mode: 'advanced', }, @@ -288,7 +333,7 @@ Return ONLY the description text - no explanations.`, placeholder: 'Group ID', condition: { field: 'operation', - value: ['create_ticket', 'update_ticket'], + value: ['create_ticket', 'update_ticket', 'update_tickets_bulk'], }, mode: 'advanced', }, @@ -296,24 +341,24 @@ Return ONLY the description text - no explanations.`, id: 'customFields', title: 'Custom Fields', type: 'long-input', - placeholder: 'JSON object with custom fields', + placeholder: '{"123456": "enterprise"}', condition: { field: 'operation', value: ['create_ticket', 'update_ticket'], }, wandConfig: { enabled: true, - prompt: `Generate a JSON object for Zendesk custom fields based on the user's description. -Custom fields use numeric IDs and can have various value types. -Format: {"id": field_id, "value": "field_value"} + prompt: `Generate a JSON object for Zendesk custom ticket fields based on the user's description. +Each key is a numeric custom field ID and each value is the value to set. +Format: {"field_id": "field_value"} Examples: - "set product to Enterprise and region to EMEA" -> - [{"id": 123456, "value": "enterprise"}, {"id": 789012, "value": "emea"}] + {"123456": "enterprise", "789012": "emea"} - "mark as VIP customer with priority support" -> - [{"id": 111111, "value": true}, {"id": 222222, "value": "priority"}] + {"111111": true, "222222": "priority"} -Return ONLY the JSON array - no explanations.`, +Return ONLY the JSON object - no explanations.`, placeholder: 'Describe the custom field values to set...', generationType: 'json-object', }, @@ -327,7 +372,7 @@ Return ONLY the JSON array - no explanations.`, required: true, condition: { field: 'operation', - value: ['create_tickets_bulk', 'update_tickets_bulk'], + value: ['create_tickets_bulk'], }, wandConfig: { enabled: true, @@ -345,10 +390,21 @@ Example structure: ] Return ONLY the JSON array - no explanations.`, - placeholder: 'Describe the tickets to create or update...', + placeholder: 'Describe the tickets to create...', generationType: 'json-object', }, }, + { + id: 'ticketIds', + title: 'Ticket IDs', + type: 'short-input', + placeholder: 'Comma-separated ticket IDs (max 100, e.g., 111, 222)', + required: true, + condition: { + field: 'operation', + value: ['update_tickets_bulk'], + }, + }, { id: 'targetTicketId', title: 'Target Ticket ID', @@ -371,6 +427,16 @@ Return ONLY the JSON array - no explanations.`, value: ['merge_tickets'], }, }, + { + id: 'targetComment', + title: 'Merge Comment', + type: 'long-input', + placeholder: 'Reason for the merge, added to the target ticket', + condition: { + field: 'operation', + value: ['merge_tickets'], + }, + }, // User fields { id: 'userId', @@ -408,6 +474,28 @@ Return ONLY the JSON array - no explanations.`, }, mode: 'advanced', }, + { + id: 'role', + title: 'Role', + type: 'short-input', + placeholder: 'end-user, agent, or admin', + condition: { + field: 'operation', + value: ['get_users', 'create_user', 'update_user'], + }, + mode: 'advanced', + }, + { + id: 'phone', + title: 'Phone', + type: 'short-input', + placeholder: '+15551234567', + condition: { + field: 'operation', + value: ['create_user', 'update_user'], + }, + mode: 'advanced', + }, { id: 'users', title: 'Users', @@ -468,13 +556,46 @@ Return ONLY the JSON array - no explanations.`, placeholder: 'Organization name', required: { field: 'operation', - value: ['autocomplete_organizations'], + value: ['autocomplete_organizations', 'create_organization'], }, condition: { field: 'operation', value: ['autocomplete_organizations', 'create_organization', 'update_organization'], }, }, + { + id: 'domainNames', + title: 'Domain Names', + type: 'short-input', + placeholder: 'Comma-separated domains (e.g., acme.com, acme.io)', + condition: { + field: 'operation', + value: ['create_organization', 'update_organization'], + }, + mode: 'advanced', + }, + { + id: 'details', + title: 'Details', + type: 'long-input', + placeholder: 'Details about the organization', + condition: { + field: 'operation', + value: ['create_organization', 'update_organization'], + }, + mode: 'advanced', + }, + { + id: 'notes', + title: 'Notes', + type: 'long-input', + placeholder: 'Notes about the organization', + condition: { + field: 'operation', + value: ['create_organization', 'update_organization'], + }, + mode: 'advanced', + }, { id: 'organizations', title: 'Organizations', @@ -556,6 +677,17 @@ Return ONLY the search query - no explanations.`, value: ['search'], }, }, + { + id: 'externalId', + title: 'External ID', + type: 'short-input', + placeholder: 'External ID from your system', + condition: { + field: 'operation', + value: ['search_users'], + }, + mode: 'advanced', + }, { id: 'sort', title: 'Sort', @@ -711,24 +843,27 @@ Return ONLY the search query - no explanations.`, throw new Error(`Unknown operation: ${params.operation}`) } }, - params: (params) => { - const { apiToken, operation, ...rest } = params - const cleanParams: Record = { apiToken } - - // Special mapping for autocomplete_organizations - if (operation === 'autocomplete_organizations' && params.organizationName) { - cleanParams.name = params.organizationName - } + params: (params, context) => { + const { apiToken, operation, organizationName, commentPublic, ...rest } = params + const cleanParams: Record = { apiToken } Object.entries(rest).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { - // Skip organizationName for autocomplete_organizations as it's mapped to 'name' - if (operation === 'autocomplete_organizations' && key === 'organizationName') { - return - } cleanParams[key] = value } }) + + // Organization tools take `name`; the block's `name` field belongs to users. + const selectedOperation = operation ?? context?.operation + if ( + selectedOperation === 'autocomplete_organizations' || + selectedOperation === 'create_organization' || + selectedOperation === 'update_organization' + ) { + cleanParams.name = organizationName || toolCallArg(context, rest.name) || undefined + } + + cleanParams.commentPublic = parseOptionalBooleanInput(commentPublic) return cleanParams }, }, @@ -775,6 +910,31 @@ Return ONLY the search query - no explanations.`, description: 'Job status for async operations (create_tickets_bulk, update_tickets_bulk, merge_tickets, create_users_bulk, update_users_bulk, create_organizations_bulk)', }, + job_status: { + type: 'json', + description: + 'Job status (id, url, status, job_type, total, progress, message, results) for bulk and merge operations', + }, + job_id: { type: 'string', description: 'Background job ID for bulk and merge operations' }, + ticket_id: { + type: 'any', + description: + 'Ticket ID: a number from get, create, and update ticket; a string from delete ticket', + }, + target_ticket_id: { + type: 'string', + description: 'Ticket the source tickets were merged into (merge_tickets)', + }, + user_id: { + type: 'any', + description: + 'User ID: a number from get, current, create, and update user; a string from delete user', + }, + organization_id: { + type: 'any', + description: + 'Organization ID: a number from get, create, and update organization; a string from delete organization', + }, // Delete operations deleted: { type: 'boolean', @@ -783,10 +943,14 @@ Return ONLY the search query - no explanations.`, // Cursor-based pagination (shared across list operations) paging: { type: 'json', - description: 'Cursor-based pagination information (after_cursor, has_more)', + description: + 'Pagination information (after_cursor, has_more, next_page); search_users and autocomplete_organizations page by number, so after_cursor is null there', }, // Metadata (shared across all operations) - metadata: { type: 'json', description: 'Operation metadata including operation type' }, + metadata: { + type: 'json', + description: 'List metadata (total_returned, has_more)', + }, }, triggers: { diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 0b1e22feaba..2d05ca153fe 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -416,7 +416,11 @@ export interface SubBlockConfig { description?: string tooltip?: string // Tooltip text displayed via info icon next to the title modalId?: string // Registry key when type is 'modal'; see sub-block/components/modal-registry.ts - value?: (params: Record) => string + /** + * Default value. Returning `undefined` (for example when the default depends on an operation + * the caller did not pass) defers to the field's own default, such as a dropdown's first option. + */ + value?: (params: Record) => string | undefined grouped?: boolean scrollable?: boolean maxHeight?: number diff --git a/apps/sim/lib/api/contracts/tools/asana.ts b/apps/sim/lib/api/contracts/tools/asana.ts index 5364dda722e..1275b57b331 100644 --- a/apps/sim/lib/api/contracts/tools/asana.ts +++ b/apps/sim/lib/api/contracts/tools/asana.ts @@ -164,7 +164,9 @@ export const asanaGetTaskBodySchema = z.object({ taskGid: z.string().nullish(), workspace: z.string().nullish(), project: z.string().nullish(), + assignee: z.string().nullish(), limit: z.union([z.string(), z.number()]).nullish(), + offset: z.string().nullish(), }) export const asanaSearchTasksBodySchema = z.object({ @@ -174,6 +176,7 @@ export const asanaSearchTasksBodySchema = z.object({ assignee: z.string().nullish(), projects: z.array(z.string()).nullish(), completed: z.boolean().nullish(), + limit: z.union([z.string(), z.number()]).nullish(), }) export const asanaUpdateTaskBodySchema = z.object({ diff --git a/apps/sim/lib/billing/credits/weekly-refresh.test.ts b/apps/sim/lib/billing/credits/weekly-refresh.test.ts index 3a95f130f2c..1268ec79733 100644 --- a/apps/sim/lib/billing/credits/weekly-refresh.test.ts +++ b/apps/sim/lib/billing/credits/weekly-refresh.test.ts @@ -14,7 +14,6 @@ import { const FROZEN_NOW = new Date('2026-08-15T00:00:00.000Z') beforeEach(() => { - drizzleOrmMock.sum.mockImplementation(() => ({ as: () => 'sum' })) vi.useFakeTimers() vi.setSystemTime(FROZEN_NOW) }) diff --git a/apps/sim/lib/core/utils/response-error.ts b/apps/sim/lib/core/utils/response-error.ts new file mode 100644 index 00000000000..a7445a0e68b --- /dev/null +++ b/apps/sim/lib/core/utils/response-error.ts @@ -0,0 +1,30 @@ +/** + * The machine-readable part of an error response, kept beside the response so + * the route handler's closing log line can be grouped by why a request was + * refused without reading the body. Never the message, which can quote user + * input. + */ +export interface ResponseErrorSummary { + /** The stable error code the body carries, e.g. `BAD_REQUEST`. */ + errorCode: string + /** For a validation failure, the request slice and its declared top-level field, e.g. `body.mapping`. */ + errorField?: string + /** The cause the refusal named (`READ_ONLY_API_KEY`) or the validation issue kind (`invalid_type`). */ + errorReason?: string +} + +/** + * Keyed by the response object, so only a response a route actually returns is + * ever read back. One built only to extract its message is never logged. + */ +const summaries = new WeakMap() + +/** Records what `response` tells its caller; a later call for the same response replaces it. */ +export function describeResponseError(response: Response, summary: ResponseErrorSummary): void { + summaries.set(response, summary) +} + +/** What `describeResponseError` recorded for `response`, if anything. */ +export function readResponseError(response: Response): ResponseErrorSummary | undefined { + return summaries.get(response) +} diff --git a/apps/sim/lib/core/utils/with-route-handler.ts b/apps/sim/lib/core/utils/with-route-handler.ts index 7a5fc3889c4..7de2af3d220 100644 --- a/apps/sim/lib/core/utils/with-route-handler.ts +++ b/apps/sim/lib/core/utils/with-route-handler.ts @@ -15,6 +15,7 @@ import { ADMISSION_RETRY_AFTER_SECONDS } from '@/lib/core/admission/transient-fa import { FILE_DELIVERY_CSP_PATH_PATTERN, getMainCSPPolicy } from '@/lib/core/security/csp' import { HttpError } from '@/lib/core/utils/http-error' import { generateRequestId } from '@/lib/core/utils/request' +import { readResponseError } from '@/lib/core/utils/response-error' import { MAX_CALL_CHAIN_DEPTH, parseCallChain, SIM_VIA_HEADER } from '@/lib/execution/call-chain' import { trackInFlightRequest } from '@/lib/monitoring/in-flight-requests' import { withPermissionGroupScope } from '@/lib/permission-groups/request-scope.server' @@ -274,9 +275,9 @@ export function withRouteHandler( const duration = Date.now() - startTime if (status >= 500) { - logger.error('Server error response', { status, duration }) + logger.error('Server error response', { status, duration, ...readResponseError(response) }) } else if (status >= 400) { - logger.warn('Client error response', { status, duration }) + logger.warn('Client error response', { status, duration, ...readResponseError(response) }) } else if (status > 0) { logger.info('OK', { status, duration }) } diff --git a/apps/sim/lib/internal/asana/operations.test.ts b/apps/sim/lib/internal/asana/operations.test.ts index c47112a442d..e79c5cf2bab 100644 --- a/apps/sim/lib/internal/asana/operations.test.ts +++ b/apps/sim/lib/internal/asana/operations.test.ts @@ -1,6 +1,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { AsanaOperationError } from '@/lib/internal/asana/errors' -import { executeAsanaAddFollowers, executeAsanaGetTask } from '@/lib/internal/asana/operations' +import { + executeAsanaAddFollowers, + executeAsanaGetTask, + executeAsanaSearchTasks, +} from '@/lib/internal/asana/operations' const AUTH = { accessToken: 'access-token' } @@ -67,4 +71,41 @@ describe('Asana operations', () => { }) expect(fetchMock).not.toHaveBeenCalled() }) + + it('sends the assignee and offset Asana requires for a workspace task list', async () => { + await executeAsanaGetTask({ ...AUTH, workspace: 'workspace1', assignee: 'me', offset: 'tok' }) + + const parsedUrl = new URL(String(fetchMock.mock.calls[0][0])) + expect(parsedUrl.searchParams.get('workspace')).toBe('workspace1') + expect(parsedUrl.searchParams.get('assignee')).toBe('me') + expect(parsedUrl.searchParams.get('offset')).toBe('tok') + }) + + it('rejects a workspace task list without an assignee and out-of-range page sizes', async () => { + await expect(executeAsanaGetTask({ ...AUTH, workspace: 'workspace1' })).rejects.toMatchObject({ + status: 400, + body: { error: 'assignee is required when listing tasks by workspace' }, + }) + await expect( + executeAsanaSearchTasks({ ...AUTH, workspace: 'workspace1', limit: 500 }) + ).rejects.toMatchObject({ status: 400 }) + await expect( + executeAsanaGetTask({ ...AUTH, project: 'project1', limit: '2.5' }) + ).rejects.toMatchObject({ status: 400 }) + await expect( + executeAsanaGetTask({ ...AUTH, project: 'project1', limit: 0 }) + ).rejects.toMatchObject({ status: 400 }) + await expect( + executeAsanaSearchTasks({ ...AUTH, workspace: 'workspace1', limit: 0 }) + ).rejects.toMatchObject({ status: 400 }) + expect(fetchMock).not.toHaveBeenCalled() + }) + + it('sends the search page size', async () => { + fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({ data: [] }))) + await executeAsanaSearchTasks({ ...AUTH, workspace: 'workspace1', limit: 25 }) + + const parsedUrl = new URL(String(fetchMock.mock.calls[0][0])) + expect(parsedUrl.searchParams.get('limit')).toBe('25') + }) }) diff --git a/apps/sim/lib/internal/asana/operations.ts b/apps/sim/lib/internal/asana/operations.ts index b0585729ffe..375aba6a659 100644 --- a/apps/sim/lib/internal/asana/operations.ts +++ b/apps/sim/lib/internal/asana/operations.ts @@ -35,6 +35,16 @@ function validateId(value: string, name: string): void { } } +/** Asana accepts page sizes from 1 to 100; reject anything else before calling the API. */ +function pageLimit(value: string | number): string { + const limit = Number(value) + if (!Number.isInteger(limit) || limit < 1 || limit > 100) { + const error = 'limit must be a whole number from 1 to 100' + throw new AsanaOperationError(error, 400, { error }) + } + return String(limit) +} + function dataObject(result: AsanaJsonObject): AsanaJsonObject { return toRecord(result.data) } @@ -289,9 +299,15 @@ export async function executeAsanaGetTask(input: AsanaGetTaskBody, signal?: Abor params.append('project', input.project) } else if (input.workspace) { validateId(input.workspace, 'workspace') + if (!input.assignee) { + const error = 'assignee is required when listing tasks by workspace' + throw new AsanaOperationError(error, 400, { error }) + } params.append('workspace', input.workspace) + params.append('assignee', input.assignee) } - params.append('limit', input.limit ? String(input.limit) : '50') + params.append('limit', input.limit != null && input.limit !== '' ? pageLimit(input.limit) : '50') + if (input.offset) params.append('offset', input.offset) params.append('opt_fields', TASK_OPT_FIELDS) const result = await client.json(`/tasks?${params.toString()}`, { method: 'GET' }, signal) return { @@ -355,6 +371,7 @@ export async function executeAsanaSearchTasks(input: AsanaSearchTasksBody, signa if (input.completed !== undefined) { params.append('completed', String(input.completed)) } + if (input.limit != null && input.limit !== '') params.append('limit', pageLimit(input.limit)) params.append('opt_fields', TASK_OPT_FIELDS) const result = await new AsanaClient(input.accessToken).json( `/workspaces/${input.workspace}/tasks/search?${params.toString()}`, diff --git a/apps/sim/lib/internal/docusign/execute-tool.ts b/apps/sim/lib/internal/docusign/execute-tool.ts index efb75859e48..a43f0dd418c 100644 --- a/apps/sim/lib/internal/docusign/execute-tool.ts +++ b/apps/sim/lib/internal/docusign/execute-tool.ts @@ -44,13 +44,15 @@ const schemas = { toDate: z.string().optional(), envelopeStatus: z.string().optional(), searchText: z.string().optional(), - count: z.string().optional(), + count: z.union([z.string(), z.number()]).transform(String).optional(), + startPosition: z.union([z.string(), z.number()]).transform(String).optional(), }), docusign_list_recipients: z.object({ ...auth, envelopeId: z.string().min(1) }), docusign_list_templates: z.object({ ...auth, searchText: z.string().optional(), - count: z.string().optional(), + count: z.union([z.string(), z.number()]).transform(String).optional(), + startPosition: z.union([z.string(), z.number()]).transform(String).optional(), }), docusign_send_envelope: z.object({ ...auth, diff --git a/apps/sim/lib/internal/docusign/operations.test.ts b/apps/sim/lib/internal/docusign/operations.test.ts index f5e002e32f5..71bfaaf28b3 100644 --- a/apps/sim/lib/internal/docusign/operations.test.ts +++ b/apps/sim/lib/internal/docusign/operations.test.ts @@ -43,6 +43,9 @@ const { mockProcessFilesToUserFiles } = fileUtilsMockFns import { DocuSignOperationError } from '@/lib/internal/docusign/errors' import { executeDocuSignDownloadDocument, + executeDocuSignGetEnvelope, + executeDocuSignListEnvelopes, + executeDocuSignListTemplates, executeDocuSignSendEnvelope, } from '@/lib/internal/docusign/operations' @@ -124,4 +127,43 @@ describe('DocuSign operations', () => { }) expect(mockUploadCopilotFile).not.toHaveBeenCalled() }) + + it('sends a document that lacks the anchor strings instead of failing on missing anchors', async () => { + await executeDocuSignSendEnvelope( + { + accessToken: 'access-token', + emailSubject: 'Sign', + signerEmail: 'a@example.com', + signerName: 'A', + file: { id: 'file-1' }, + }, + CONTEXT + ) + + const body = JSON.parse(mocks.json.mock.calls[0][1].body) + const tabs = body.recipients.signers[0].tabs + expect(tabs.signHereTabs[0].anchorIgnoreIfNotPresent).toBe('true') + expect(tabs.dateSignedTabs[0].anchorIgnoreIfNotPresent).toBe('true') + }) + + it.each([ + ['envelopes', executeDocuSignListEnvelopes], + ['templates', executeDocuSignListTemplates], + ] as const)('pages %s with start_position', async (_name, execute) => { + await execute({ accessToken: 'access-token', count: '50', startPosition: '100' }, CONTEXT) + + const query = new URL(`https://x${mocks.json.mock.calls[0][0]}`).searchParams + expect(query.get('start_position')).toBe('100') + expect(query.get('count')).toBe('50') + }) + + it.each(['..', 'env-1/../../users'])( + 'rejects envelope id %s before contacting DocuSign', + async (envelopeId) => { + await expect( + executeDocuSignGetEnvelope({ accessToken: 'access-token', envelopeId }, CONTEXT) + ).rejects.toMatchObject({ status: 400 }) + expect(mocks.create).not.toHaveBeenCalled() + } + ) }) diff --git a/apps/sim/lib/internal/docusign/operations.ts b/apps/sim/lib/internal/docusign/operations.ts index e525b66da99..b97066d01fe 100644 --- a/apps/sim/lib/internal/docusign/operations.ts +++ b/apps/sim/lib/internal/docusign/operations.ts @@ -20,6 +20,7 @@ import type { DocuSignSendEnvelopeParams, DocuSignVoidEnvelopeParams, } from '@/tools/docusign/types' +import { safeUrlPathSegment } from '@/tools/url-path' const logger = createLogger('DocuSignOperations') const MAX_LEGACY_INLINE_DOCUMENT_BYTES = 7 * 1024 * 1024 @@ -37,6 +38,14 @@ function jsonBody(data: Record): RequestInit { return { method: 'POST', body: JSON.stringify(data) } } +function pathSegment(value: string, paramName: string): string { + try { + return safeUrlPathSegment(value, paramName) + } catch (error) { + throw new DocuSignOperationError(getErrorMessage(error, `Invalid ${paramName}`), 400) + } +} + async function client( input: { accessToken: string; instanceUrl?: string }, signal?: AbortSignal @@ -141,6 +150,7 @@ export async function executeDocuSignSendEnvelope( anchorUnits: 'pixels', anchorXOffset: '0', anchorYOffset: '0', + anchorIgnoreIfNotPresent: 'true', }, ], dateSignedTabs: [ @@ -149,6 +159,7 @@ export async function executeDocuSignSendEnvelope( anchorUnits: 'pixels', anchorXOffset: '0', anchorYOffset: '0', + anchorIgnoreIfNotPresent: 'true', }, ], }, @@ -191,9 +202,10 @@ export async function executeDocuSignGetEnvelope( input: DocuSignGetEnvelopeParams, context: DocuSignOperationContext ) { + const envelopeId = pathSegment(input.envelopeId, 'envelopeId') const provider = await client(input, context.signal) return provider.json( - `/envelopes/${input.envelopeId.trim()}?include=recipients,documents`, + `/envelopes/${envelopeId}?include=recipients,documents`, {}, 'DocuSign envelope response', 'Failed to get envelope', @@ -216,6 +228,7 @@ export async function executeDocuSignListEnvelopes( if (input.envelopeStatus) query.append('status', input.envelopeStatus) if (input.searchText) query.append('search_text', input.searchText) if (input.count) query.append('count', input.count) + if (input.startPosition) query.append('start_position', input.startPosition) const provider = await client(input, context.signal) return provider.json( `/envelopes?${query}`, @@ -230,9 +243,10 @@ export async function executeDocuSignVoidEnvelope( input: DocuSignVoidEnvelopeParams, context: DocuSignOperationContext ) { + const envelopeId = pathSegment(input.envelopeId, 'envelopeId') const provider = await client(input, context.signal) await provider.json( - `/envelopes/${input.envelopeId.trim()}`, + `/envelopes/${envelopeId}`, { method: 'PUT', body: JSON.stringify({ status: 'voided', voidedReason: input.voidedReason }) }, 'DocuSign void envelope response', 'Failed to void envelope', @@ -245,10 +259,11 @@ export async function executeDocuSignDownloadDocument( input: DocuSignDownloadDocumentParams, context: DocuSignOperationContext ) { - const documentId = input.documentId || 'combined' + const envelopeId = pathSegment(input.envelopeId, 'envelopeId') + const documentId = pathSegment(input.documentId || 'combined', 'documentId') const provider = await client(input, context.signal) const { buffer, contentType, fileName } = await provider.document( - input.envelopeId.trim(), + envelopeId, documentId, context.signal ) @@ -281,6 +296,7 @@ export async function executeDocuSignListTemplates( const query = new URLSearchParams() if (input.searchText) query.append('search_text', input.searchText) if (input.count) query.append('count', input.count) + if (input.startPosition) query.append('start_position', input.startPosition) const suffix = query.size ? `?${query}` : '' const provider = await client(input, context.signal) return provider.json( @@ -296,9 +312,10 @@ export async function executeDocuSignListRecipients( input: DocuSignListRecipientsParams, context: DocuSignOperationContext ) { + const envelopeId = pathSegment(input.envelopeId, 'envelopeId') const provider = await client(input, context.signal) return provider.json( - `/envelopes/${input.envelopeId.trim()}/recipients`, + `/envelopes/${envelopeId}/recipients`, {}, 'DocuSign recipients response', 'Failed to list recipients', diff --git a/apps/sim/lib/internal/square/operations.test.ts b/apps/sim/lib/internal/square/operations.test.ts index 49508c67358..7fcb3122fae 100644 --- a/apps/sim/lib/internal/square/operations.test.ts +++ b/apps/sim/lib/internal/square/operations.test.ts @@ -64,6 +64,7 @@ describe('executeSquareCreateCatalogImage', () => { idempotency_key: 'stable-key', object_id: 'item-1', }) + expect(formData.get('image_file')).toBeInstanceOf(Blob) expect(vi.mocked(fetch).mock.calls[0][0]).toBe('https://connect.squareup.com/v2/catalog/images') }) diff --git a/apps/sim/lib/internal/square/operations.ts b/apps/sim/lib/internal/square/operations.ts index 21e85f4fd4c..46fad0ec68b 100644 --- a/apps/sim/lib/internal/square/operations.ts +++ b/apps/sim/lib/internal/square/operations.ts @@ -64,7 +64,7 @@ export async function executeSquareCreateCatalogImage( const formData = new FormData() formData.append('request', JSON.stringify(imageRequest)) formData.append( - 'file', + 'image_file', new Blob([new Uint8Array(fileBuffer)], { type: userFile.type || 'application/octet-stream', }), diff --git a/apps/sim/lib/internal/workday/client.test.ts b/apps/sim/lib/internal/workday/client.test.ts index bba8f4acd98..e2bd0a5da9f 100644 --- a/apps/sim/lib/internal/workday/client.test.ts +++ b/apps/sim/lib/internal/workday/client.test.ts @@ -4,6 +4,7 @@ import { createWorkdaySoapClient, extractRefId, } from '@/lib/internal/workday/client' +import { executeWorkdayGetOrganizations } from '@/lib/internal/workday/operations' const SUCCESS_RESPONSE = ` @@ -39,6 +40,31 @@ describe('Workday SOAP client', () => { ) }) + it('sends Get_Organizations criteria before the response filter, in schema order', async () => { + fetchMock.mockResolvedValue( + new Response( + '', + { status: 200 } + ) + ) + + await executeWorkdayGetOrganizations({ + tenantUrl: 'https://wd2-impl-services1.workday.com', + tenant: 'example', + username: 'user', + password: 'password', + type: 'Supervisory', + }) + + const body = String(fetchMock.mock.calls[0]?.[1]?.body) + const criteria = body.indexOf('') + const filter = body.indexOf('') + const group = body.indexOf('') + expect(criteria).toBeGreaterThan(-1) + expect(criteria).toBeLessThan(filter) + expect(filter).toBeLessThan(group) + }) + it('passes cancellation to fetch, escapes XML, and parses SOAP responses', async () => { fetchMock.mockResolvedValue(new Response(SUCCESS_RESPONSE, { status: 200 })) const controller = new AbortController() @@ -73,4 +99,36 @@ describe('Workday SOAP client', () => { expect(extractRefId(worker.Worker_Reference)).toBe('worker-1') expect(result.Response_Results?.Total_Results).toBe('1') }) + + it.each([ + ['Hire_EmployeeAsync', 'Hire_Employee_Event_Response'], + ['Terminate_EmployeeAsync', 'Terminate_Employee_Event_Response'], + ] as const)('parses %s from its %s element', async (method, responseElement) => { + fetchMock.mockResolvedValue( + new Response( + ` + + + + event-1 + 04345 + + +`, + { status: 200 } + ) + ) + const client = await createWorkdaySoapClient( + 'https://wd2-impl-services1.workday.com', + 'example', + 'staffing', + 'user', + 'password' + ) + + const [result] = await client[method]({}) + + expect(extractRefId(result.Event_Reference)).toBe('event-1') + expect(extractRefId(result.Employee_Reference)).toBe('04345') + }) }) diff --git a/apps/sim/lib/internal/workday/client.ts b/apps/sim/lib/internal/workday/client.ts index faa15c6499e..2e578c8f658 100644 --- a/apps/sim/lib/internal/workday/client.ts +++ b/apps/sim/lib/internal/workday/client.ts @@ -60,24 +60,19 @@ interface WorkdayWorkerDataSoap { Organization_Data?: Record } +/** + * `Worker_Compensation_Data` children returned by Get_Workers with `Include_Compensation`. + * Element names come from the Human_Resources WSDL (`Worker_Compensation_DataType`). + */ export interface WorkdayCompensationDataSoap { - Employee_Base_Pay_Plan_Assignment_Data?: - | WorkdayCompensationPlanSoap - | WorkdayCompensationPlanSoap[] - Employee_Salary_Unit_Plan_Assignment_Data?: - | WorkdayCompensationPlanSoap - | WorkdayCompensationPlanSoap[] - Employee_Bonus_Plan_Assignment_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] - Employee_Allowance_Plan_Assignment_Data?: - | WorkdayCompensationPlanSoap - | WorkdayCompensationPlanSoap[] - Employee_Commission_Plan_Assignment_Data?: - | WorkdayCompensationPlanSoap - | WorkdayCompensationPlanSoap[] - Employee_Stock_Plan_Assignment_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] - Employee_Period_Salary_Plan_Assignment_Data?: - | WorkdayCompensationPlanSoap - | WorkdayCompensationPlanSoap[] + Salary_and_Hourly_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Unit_Salary_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Allowance_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Unit_Allowance_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Bonus_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Commission_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Stock_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] + Period_Salary_Plan_Data?: WorkdayCompensationPlanSoap | WorkdayCompensationPlanSoap[] } export interface WorkdayCompensationPlanSoap { @@ -85,6 +80,7 @@ export interface WorkdayCompensationPlanSoap { Amount?: number | string Per_Unit_Amount?: number | string Individual_Target_Amount?: number | string + Target_Amount?: number | string Currency_Reference?: WorkdayReference Frequency_Reference?: WorkdayReference } @@ -94,11 +90,11 @@ export interface WorkdayCompensationPlanSoap { */ export interface WorkdayOrganizationSoap { Organization_Reference?: WorkdayReference - Organization_Descriptor?: string Organization_Data?: WorkdayOrganizationDataSoap } interface WorkdayOrganizationDataSoap { + Name?: string Organization_Type_Reference?: WorkdayReference Organization_Subtype_Reference?: WorkdayReference Inactive?: boolean | string @@ -159,6 +155,15 @@ const WD_OPERATIONS = [ type WorkdayOperation = (typeof WD_OPERATIONS)[number] +/** + * Business-process operations whose WSDL response element is `_Event_Response` + * instead of `_Response`. + */ +const EVENT_RESPONSE_OPERATIONS: ReadonlySet = new Set([ + 'Hire_Employee', + 'Terminate_Employee', +]) + type SoapOperationFn = ( args: Record ) => Promise<[WorkdaySoapResult, string, Record, string]> @@ -601,7 +606,10 @@ async function callOperation( throw new Error(`Workday SOAP request failed (HTTP ${response.status})`) } - const responseElement = findFirst(root, `${operation}_Response`) + const responseElementName = EVENT_RESPONSE_OPERATIONS.has(operation) + ? `${operation}_Event_Response` + : `${operation}_Response` + const responseElement = findFirst(root, responseElementName) const value = (responseElement ? nodeToValue(responseElement) : {}) as WorkdaySoapResult return [value, responseText, {}, envelope] diff --git a/apps/sim/lib/internal/workday/operations.test.ts b/apps/sim/lib/internal/workday/operations.test.ts index 9a38bab2283..d4c44012933 100644 --- a/apps/sim/lib/internal/workday/operations.test.ts +++ b/apps/sim/lib/internal/workday/operations.test.ts @@ -34,7 +34,15 @@ vi.mock('@/lib/internal/workday/client', () => ({ }), })) -import { executeWorkdayListWorkers } from '@/lib/internal/workday/operations' +import { + executeWorkdayCreatePrehire, + executeWorkdayGetCompensation, + executeWorkdayGetOrganizations, + executeWorkdayHire, + executeWorkdayListWorkers, + executeWorkdayTerminate, + executeWorkdayUpdateWorker, +} from '@/lib/internal/workday/operations' const CREDENTIALS = { tenantUrl: 'https://wd2-impl-services1.workday.com', @@ -102,4 +110,131 @@ describe('Workday operations', () => { }, }) }) + + function firstCallArgs(mock: typeof clientMocks.Get_WorkersAsync): Record { + const call = mock.mock.calls[0] + if (!call) throw new Error('Expected the SOAP operation to be called') + return call[0] as Record + } + + it('reads compensation plans from the Worker_Compensation_DataType element names', async () => { + clientMocks.Get_WorkersAsync.mockResolvedValue([ + { + Response_Data: { + Worker: { + Worker_Data: { + Compensation_Data: { + Salary_and_Hourly_Data: { + Compensation_Plan_Reference: { + ID: { $value: 'base' }, + attributes: { Descriptor: 'Base Pay' }, + }, + Amount: '120000', + }, + Commission_Plan_Data: { + Compensation_Plan_Reference: { ID: { $value: 'commission' } }, + Target_Amount: '15000', + }, + }, + }, + }, + }, + }, + ]) + + const result = await executeWorkdayGetCompensation({ ...CREDENTIALS, workerId: '04345' }) + + expect(result.output.compensationPlans).toEqual([ + expect.objectContaining({ id: 'base', planName: 'Base Pay', amount: 120000 }), + expect.objectContaining({ id: 'commission', amount: 15000 }), + ]) + }) + + it('reads the organization descriptor attribute and clamps the page size', async () => { + clientMocks.Get_OrganizationsAsync.mockResolvedValue([ + { + Response_Data: { + Organization: { + Organization_Reference: { + ID: { $value: 'org-1' }, + attributes: { Descriptor: 'Engineering' }, + }, + }, + }, + }, + ]) + + const result = await executeWorkdayGetOrganizations({ ...CREDENTIALS, limit: 5000 }) + + expect(result.output.organizations[0]?.descriptor).toBe('Engineering') + expect(firstCallArgs(clientMocks.Get_OrganizationsAsync).Response_Filter).toEqual({ + Page: 1, + Count: 999, + }) + }) + + it('sends personal information changes under Change_Personal_Information_Data', async () => { + await executeWorkdayUpdateWorker({ + ...CREDENTIALS, + workerId: '04345', + fields: { Marital_Status_Reference: 'Single' }, + }) + + expect(firstCallArgs(clientMocks.Change_Personal_InformationAsync)).toHaveProperty( + 'Change_Personal_Information_Data.Personal_Information_Data', + { Marital_Status_Reference: 'Single' } + ) + }) + + it('sends pre-hire contact data in schema order with a qualified Public attribute', async () => { + await executeWorkdayCreatePrehire({ + ...CREDENTIALS, + legalName: 'Ada Lovelace', + email: 'ada@example.com', + phoneNumber: '555-0100', + address: '1 Main St', + }) + + const args = firstCallArgs(clientMocks.Put_ApplicantAsync) as { + Applicant_Data: { Personal_Data: { Contact_Data: Record } } + } + const contactData = args.Applicant_Data.Personal_Data.Contact_Data + expect(Object.keys(contactData)).toEqual(['Address_Data', 'Phone_Data', 'Email_Address_Data']) + expect(contactData.Address_Data?.[0]).toMatchObject({ + Address_Line_Data: { attributes: { 'wd:Type': 'ADDRESS_LINE_1' }, $value: '1 Main St' }, + Usage_Data: { attributes: { 'wd:Public': 'true' } }, + }) + }) + + it('sends Last_Day_of_Work before the termination reason', async () => { + await executeWorkdayTerminate({ + ...CREDENTIALS, + workerId: '04345', + terminationDate: '2026-12-01', + reason: 'Resignation', + }) + + const args = firstCallArgs(clientMocks.Terminate_EmployeeAsync) as { + Terminate_Employee_Data: { Terminate_Event_Data: Record } + } + expect(Object.keys(args.Terminate_Employee_Data.Terminate_Event_Data)).toEqual([ + 'Last_Day_of_Work', + 'Primary_Reason_Reference', + 'Notification_Date', + ]) + }) + + it('sends the Position_Details element the hire event requires', async () => { + await executeWorkdayHire({ + ...CREDENTIALS, + preHireId: 'APP-1', + positionId: 'P-1', + hireDate: '2026-12-01', + }) + + expect(firstCallArgs(clientMocks.Hire_EmployeeAsync)).toHaveProperty( + 'Hire_Employee_Data.Hire_Employee_Event_Data.Position_Details', + {} + ) + }) }) diff --git a/apps/sim/lib/internal/workday/operations.ts b/apps/sim/lib/internal/workday/operations.ts index efa1487994a..b0b2f7afc67 100644 --- a/apps/sim/lib/internal/workday/operations.ts +++ b/apps/sim/lib/internal/workday/operations.ts @@ -56,6 +56,15 @@ function workerSummary(worker: WorkdayWorkerSoap) { } } +/** + * Clamps a page size to the 1-999 range `Response_Filter.Count` accepts (Human_Resources + * WSDL `Response_FilterType`), defaulting to 20 when the value is absent or not a number. + */ +function responseFilterCount(limit: number | undefined): number { + if (limit === undefined || !Number.isFinite(limit)) return 20 + return Math.min(999, Math.max(1, Math.trunc(limit))) +} + export async function executeWorkdayGetWorker(input: WorkdayGetWorkerBody, signal?: AbortSignal) { const client = await createClient(input, 'humanResources', signal) const [result] = await client.Get_WorkersAsync({ @@ -96,7 +105,7 @@ export async function executeWorkdayListWorkers( signal?: AbortSignal ) { const client = await createClient(input, 'humanResources', signal) - const limit = input.limit ?? 20 + const limit = responseFilterCount(input.limit) const offset = input.offset ?? 0 const page = offset > 0 ? Math.floor(offset / limit) + 1 : 1 const [result] = await client.Get_WorkersAsync({ @@ -132,15 +141,18 @@ export async function executeWorkdayCreatePrehire( throw new WorkdayOperationError('Legal name must include both a first name and last name', 400) } + const countryCode = input.countryCode ?? 'US' + const workUsageData = { + attributes: { 'wd:Public': 'true' }, + Type_Data: { Type_Reference: wdRef('Communication_Usage_Type_ID', 'WORK') }, + } const contactData: Record = {} - if (input.email) { - contactData.Email_Address_Data = [ + if (input.address) { + contactData.Address_Data = [ { - Email_Address: input.email, - Usage_Data: { - Type_Data: { Type_Reference: wdRef('Communication_Usage_Type_ID', 'WORK') }, - Public: true, - }, + Country_Reference: wdRef('ISO_3166-1_Alpha-2_Code', countryCode), + Address_Line_Data: { attributes: { 'wd:Type': 'ADDRESS_LINE_1' }, $value: input.address }, + Usage_Data: workUsageData, }, ] } @@ -149,23 +161,12 @@ export async function executeWorkdayCreatePrehire( { Phone_Number: input.phoneNumber, Phone_Device_Type_Reference: wdRef('Phone_Device_Type_ID', 'Landline'), - Usage_Data: { - Type_Data: { Type_Reference: wdRef('Communication_Usage_Type_ID', 'WORK') }, - Public: true, - }, + Usage_Data: workUsageData, }, ] } - if (input.address) { - contactData.Address_Data = [ - { - Formatted_Address: input.address, - Usage_Data: { - Type_Data: { Type_Reference: wdRef('Communication_Usage_Type_ID', 'WORK') }, - Public: true, - }, - }, - ] + if (input.email) { + contactData.Email_Address_Data = [{ Email_Address: input.email, Usage_Data: workUsageData }] } const client = await createClient(input, 'recruiting', signal) @@ -175,13 +176,13 @@ export async function executeWorkdayCreatePrehire( Name_Data: { Legal_Name_Data: { Name_Detail_Data: { - Country_Reference: wdRef('ISO_3166-1_Alpha-2_Code', input.countryCode ?? 'US'), + Country_Reference: wdRef('ISO_3166-1_Alpha-2_Code', countryCode), First_Name: firstName, Last_Name: lastName, }, }, }, - Contact_Information_Data: contactData, + Contact_Data: contactData, }, }, }) @@ -207,6 +208,7 @@ export async function executeWorkdayHire(input: WorkdayHireBody, signal?: AbortS Hire_Employee_Event_Data: { Employee_Type_Reference: wdRef('Employee_Type_ID', input.employeeType ?? 'Regular'), First_Day_of_Work: input.hireDate, + Position_Details: {}, }, }, }) @@ -230,7 +232,7 @@ export async function executeWorkdayUpdateWorker( const client = await createClient(input, 'humanResources', signal) const [result] = await client.Change_Personal_InformationAsync({ Business_Process_Parameters: { Auto_Complete: true, Run_Now: true }, - Change_Personal_Information_Business_Process_Data: { + Change_Personal_Information_Data: { Person_Reference: wdRef('Employee_ID', input.workerId), Personal_Information_Data: input.fields, }, @@ -275,11 +277,10 @@ export async function executeWorkdayGetOrganizations( signal?: AbortSignal ) { const client = await createClient(input, 'humanResources', signal) - const limit = input.limit ?? 20 + const limit = responseFilterCount(input.limit) const offset = input.offset ?? 0 const page = offset > 0 ? Math.floor(offset / limit) + 1 : 1 const [result] = await client.Get_OrganizationsAsync({ - Response_Filter: { Page: page, Count: limit }, Request_Criteria: input.type ? { Organization_Type_Reference: { @@ -287,6 +288,7 @@ export async function executeWorkdayGetOrganizations( }, } : undefined, + Response_Filter: { Page: page, Count: limit }, Response_Group: { Include_Hierarchy_Data: true }, }) signal?.throwIfAborted() @@ -299,7 +301,10 @@ export async function executeWorkdayGetOrganizations( const inactive = parseSoapBoolean(organization.Organization_Data?.Inactive) return { id: extractRefId(organization.Organization_Reference) ?? null, - descriptor: organization.Organization_Descriptor ?? null, + descriptor: + organization.Organization_Reference?.attributes?.Descriptor ?? + organization.Organization_Data?.Name ?? + null, type: extractRefId(organization.Organization_Data?.Organization_Type_Reference) ?? null, subtype: extractRefId(organization.Organization_Data?.Organization_Subtype_Reference) ?? null, isActive: inactive == null ? null : !inactive, @@ -379,18 +384,20 @@ export async function executeWorkdayGetCompensation( parseSoapNumber(plan.Amount) ?? parseSoapNumber(plan.Per_Unit_Amount) ?? parseSoapNumber(plan.Individual_Target_Amount) ?? + parseSoapNumber(plan.Target_Amount) ?? null, currency: extractRefId(plan.Currency_Reference) ?? null, frequency: extractRefId(plan.Frequency_Reference) ?? null, }) const planTypeKeys: (keyof WorkdayCompensationDataSoap)[] = [ - 'Employee_Base_Pay_Plan_Assignment_Data', - 'Employee_Salary_Unit_Plan_Assignment_Data', - 'Employee_Bonus_Plan_Assignment_Data', - 'Employee_Allowance_Plan_Assignment_Data', - 'Employee_Commission_Plan_Assignment_Data', - 'Employee_Stock_Plan_Assignment_Data', - 'Employee_Period_Salary_Plan_Assignment_Data', + 'Salary_and_Hourly_Data', + 'Unit_Salary_Plan_Data', + 'Allowance_Plan_Data', + 'Unit_Allowance_Plan_Data', + 'Bonus_Plan_Data', + 'Commission_Plan_Data', + 'Stock_Plan_Data', + 'Period_Salary_Plan_Data', ] const compensationPlans = planTypeKeys.flatMap((key) => normalizeSoapArray(compensationData?.[key]).map(mapPlan) @@ -406,8 +413,8 @@ export async function executeWorkdayTerminate(input: WorkdayTerminateBody, signa Employee_Reference: wdRef('Employee_ID', input.workerId), Termination_Date: input.terminationDate, Terminate_Event_Data: { - Primary_Reason_Reference: wdRef('Termination_Subcategory_ID', input.reason), Last_Day_of_Work: input.lastDayOfWork ?? input.terminationDate, + Primary_Reason_Reference: wdRef('Termination_Subcategory_ID', input.reason), Notification_Date: input.notificationDate ?? input.terminationDate, }, }, diff --git a/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts b/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts index d9d4c674adc..cd69d176551 100644 --- a/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts @@ -1,6 +1,7 @@ import { writeFileSync } from 'node:fs' import { db } from '@sim/db' import { document, knowledgeBase, organization, user, workspace } from '@sim/db/schema' +import { explainCaptured } from '@sim/db/testing/explain' import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' @@ -14,12 +15,14 @@ import { getWorkspaceKnowledgeBases } from '@/lib/knowledge/service' /** * A small KB page must not count the rest of its workspace or other tenants before applying - * its limit. Real query plans catch this even when warm caches hide it from a timing test. + * its limit, and many bases must not read the documents other tenants share a token with. Real query plans catch this even when warm caches hide it from a timing test. * The same read must retain ACL/lifecycle filtering, empty bases, and keyset continuity. */ const ids = createKnowledgeAclFixtureIds() const foreign = createKnowledgeAclFixtureIds() const manyBases = createKnowledgeAclFixtureIds() +const archivedBases = createKnowledgeAclFixtureIds() +const ARCHIVED_BASE_COUNT = 600 const offPageId = generateId() const emptyId = generateId() const archivedId = generateId() @@ -118,6 +121,16 @@ beforeAll(async () => { 'https://fixture.invalid/first', 1, 'text/plain', ARRAY['ws'], 13), (${generateId()}, ${`${manyBases.knowledgeBaseId}-10000`}, 'last.txt', 'https://fixture.invalid/last', 1, 'text/plain', ARRAY['ws'], 17)`) + await seedKnowledgeAclFixture(archivedBases, { connectorType: 'google_drive' }) + await db.execute(sql`INSERT INTO knowledge_base (id, workspace_id, user_id, name, deleted_at) + SELECT ${archivedBases.knowledgeBaseId} || '-' || n, ${archivedBases.workspaceId}, + ${archivedBases.aliceId}, 'Archived fixture ' || n, now() + FROM generate_series(1, ${ARCHIVED_BASE_COUNT}) AS n`) + await db.execute(sql`INSERT INTO document + (id, knowledge_base_id, filename, file_url, file_size, mime_type, acl, token_count, archived_at) + SELECT ${archivedBases.knowledgeBaseId} || '-doc-' || n, ${archivedBases.knowledgeBaseId} || '-' || n, + 'archived.txt', 'https://fixture.invalid/archived', 1, 'text/plain', ARRAY['ws'], 1, now() + FROM generate_series(1, ${ARCHIVED_BASE_COUNT}) AS n`) await db.execute(sql`ANALYZE knowledge_base`) await db.execute(sql`ANALYZE document`) }, 60_000) @@ -126,7 +139,7 @@ afterAll(async () => { const reportPath = process.env.KNOWLEDGE_BASE_LIST_REPORT_PATH if (reportPath) writeFileSync(reportPath, JSON.stringify(reports, null, 2)) try { - for (const fixture of [ids, foreign, manyBases]) { + for (const fixture of [ids, foreign, manyBases, archivedBases]) { await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(inArray(user.id, [fixture.aliceId, fixture.bobId])) @@ -172,6 +185,34 @@ describe('knowledge base list counts on real Postgres', () => { } ) + /** + * Bases whose documents are all archived, beside thousands of other tenants' live `ws` + * documents. No small fixture reproduces the production estimates that once turned this count + * into a scan of every tenant's documents (see `countDocumentsByKnowledgeBase`), so this pins + * the shape that rules that plan out: each index read of `document` seeks one base. + */ + it('counts many archived bases one base at a time', async () => { + const list = await getWorkspaceKnowledgeBases(archivedBases.workspaceId, 'archived', { + countsFor: access, + }) + expect(list.data).toHaveLength(ARCHIVED_BASE_COUNT) + expect(list.data.every((kb) => kb.docCount === 0 && kb.tokenCount === 0)).toBe(true) + const nodes = await explainCaptured( + db, + () => + getWorkspaceKnowledgeBases(archivedBases.workspaceId, 'archived', { countsFor: access }), + { match: /"document"/ } + ) + expect( + nodes.some((node) => node['Node Type'] === 'Seq Scan' && node['Relation Name'] === 'document') + ).toBe(false) + const documentIndexReads = nodes.filter((node) => node['Index Name']?.startsWith('doc_')) + expect(documentIndexReads.length).toBeGreaterThan(0) + expect(documentIndexReads.map((node) => node['Index Cond'])).toEqual( + documentIndexReads.map(() => '(knowledge_base_id = knowledge_base.id)') + ) + }) + it('keeps empty KBs and count visibility through pagination and unpaged reads', async () => { const first = await getWorkspaceKnowledgeBases(ids.workspaceId, 'active', { countsFor: access, diff --git a/apps/sim/lib/knowledge/application/search-diagnostics.ts b/apps/sim/lib/knowledge/application/search-diagnostics.ts index a1e444d726c..1b2a289d393 100644 --- a/apps/sim/lib/knowledge/application/search-diagnostics.ts +++ b/apps/sim/lib/knowledge/application/search-diagnostics.ts @@ -3,10 +3,18 @@ import type { knowledgeOperations } from '@/lib/knowledge/application/operations import type { SearchKnowledgeInput } from '@/lib/knowledge/application/search' import { measureSearchStage, + type SearchDiagnosticMetadata, type SearchStage, withSearchDiagnostics, } from '@/lib/knowledge/search/diagnostics' +function scopeKind(input: { + workspaceId?: string | null + organizationId?: string | null +}): SearchDiagnosticMetadata['scopeKind'] { + return input.organizationId ? 'organization' : input.workspaceId ? 'workspace' : undefined +} + /** Preserve the operation and authorization contract while timing the whole use case. */ export function instrumentSearchUseCase< I extends Omit & { @@ -25,11 +33,7 @@ export function instrumentSearchUseCase< { surface: args.input.surface ?? 'other', principalKind: args.principal.kind, - scopeKind: args.input.organizationId - ? 'organization' - : args.input.workspaceId - ? 'workspace' - : undefined, + scopeKind: scopeKind(args.input), topK: args.input.topK, documentFilterCount: args.input.filters?.documentIds?.length ?? 0, hasSourceFilter: Boolean(args.input.filters?.source), @@ -48,3 +52,35 @@ export function instrumentSearchUseCase< ), } } + +/** + * Times a document read as one trace, so each read logs its own summary: the source, the + * document's kind and size, and the window returned. A read inside a traced tool call adds to + * that call's trace instead. + */ +export function instrumentReadUseCase< + I extends { + surface?: SearchDiagnosticMetadata['surface'] + workspaceId?: string | null + organizationId?: string | null + startOffset?: number + }, + R, +>( + useCase: AuthorizingUseCase +): AuthorizingUseCase { + return { + ...useCase, + execute: (args) => + withSearchDiagnostics( + { + operation: 'read_document', + ...(args.input.surface ? { surface: args.input.surface } : {}), + principalKind: args.principal.kind, + scopeKind: scopeKind(args.input), + continued: Boolean(args.input.startOffset), + }, + () => measureSearchStage('document_read', () => useCase.execute(args)) + ), + } +} diff --git a/apps/sim/lib/knowledge/mcp/server.ts b/apps/sim/lib/knowledge/mcp/server.ts index b4dc2b4aa2c..80641cd8ed3 100644 --- a/apps/sim/lib/knowledge/mcp/server.ts +++ b/apps/sim/lib/knowledge/mcp/server.ts @@ -297,6 +297,7 @@ export function createKnowledgeMcpServer(context: KnowledgeMcpContext): McpServe principal, input: { ...options, + surface: 'mcp', documentId: id, organizationId, scaledWindow: true, diff --git a/apps/sim/lib/knowledge/search/diagnostics.test.ts b/apps/sim/lib/knowledge/search/diagnostics.test.ts index 59fe303e715..021c86fa509 100644 --- a/apps/sim/lib/knowledge/search/diagnostics.test.ts +++ b/apps/sim/lib/knowledge/search/diagnostics.test.ts @@ -1,13 +1,17 @@ import { createDeferred } from '@sim/testing/helpers' import { getMockLogger } from '@sim/testing/mocks/logger.mock' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { OrchestrationError } from '@/lib/core/orchestration/types' import { + describeSearchRequest, + documentKindLabel, measureSearchStage, recordSearchProviderResult, withSearchDiagnostics, withSearchProviderDiagnostics, withSearchRequestDiagnostics, } from '@/lib/knowledge/search/diagnostics' +import { LiveReadError } from '@/lib/sim-search/live/read-error' const logs = getMockLogger('KnowledgeSearchDiagnostics') @@ -171,4 +175,112 @@ describe('search pipeline diagnostics', () => { ) expect(JSON.stringify(logs.info.mock.calls)).not.toContain('private preview content') }) + + it('describes a search request without its text, modifiers, cursor, or account', () => { + const shape = describeSearchRequest({ + query: ' private launch plan ', + filters: { + startDate: '2026-10-30T00:00:00Z', + endDate: '2026-11-13T12:00:00Z', + sortBy: 'newest', + }, + nativeQueries: [ + { + provider: 'slack', + query: 'private decision', + modifiers: 'in:#private-channel', + cursor: 'private-cursor', + accountId: 'private-account', + termClauses: ['private term'], + }, + { provider: 'jira', query: '', kind: 'issues' }, + ], + }) + expect(shape).toEqual({ + queryTermCount: 3, + sortBy: 'newest', + dateWindowDays: 14.5, + nativeQueries: [ + { + provider: 'slack', + hasText: true, + hasModifiers: true, + hasCursor: true, + hasProject: false, + targetsAccount: true, + keywordOnly: false, + termClauseCount: 1, + }, + { + provider: 'jira', + kind: 'issues', + hasText: false, + hasModifiers: false, + hasCursor: false, + hasProject: false, + targetsAccount: false, + keywordOnly: false, + termClauseCount: 0, + }, + ], + }) + expect(JSON.stringify(shape)).not.toContain('private') + }) + + it('keeps a document kind only when it is a bounded token', () => { + expect(documentKindLabel('file')).toBe('file') + expect(documentKindLabel('application/vnd.google-apps.document')).toBe( + 'application/vnd.google-apps.document' + ) + expect(documentKindLabel('Quarterly plan for a private customer')).toBe('other') + expect(documentKindLabel('ProjectAtlas_Q3')).toBe('other') + expect(documentKindLabel('atlas-launch-notes')).toBe('other') + expect(documentKindLabel('notes/atlas')).toBe('other') + expect(documentKindLabel(undefined)).toBeUndefined() + }) + + it('names the class of a failure that ends a trace, never its message', async () => { + const failures = [ + new OrchestrationError('not_found', 'private document title is gone'), + new LiveReadError('private provider text', true), + new Error('private unexpected text'), + ] + for (const error of failures) + await expect( + withSearchDiagnostics({ operation: 'read_document' }, async () => { + throw error + }) + ).rejects.toBe(error) + const summaries = logs.info.mock.calls + .filter(([message]) => message === 'Knowledge search completed') + .slice(-3) + .map(([, fields]) => fields?.failure) + expect(summaries).toEqual([ + { reason: 'not_found' }, + { reason: 'provider_retryable' }, + { reason: 'unexpected_error' }, + ]) + const logged = JSON.stringify(logs.info.mock.calls) + for (const error of failures) expect(logged).not.toContain(error.message) + }) + + it('keeps the failure class when an outer tool call turns the error into a result', async () => { + await withSearchDiagnostics({ surface: 'copilot' }, async () => { + try { + await withSearchDiagnostics({ operation: 'read_document' }, async () => { + throw new OrchestrationError('not_found', 'private document title is gone') + }) + } catch { + return { success: false } + } + }) + expect(logs.info).toHaveBeenLastCalledWith( + 'Knowledge search completed', + expect.objectContaining({ + surface: 'copilot', + outcome: 'error', + failure: { reason: 'not_found' }, + }) + ) + }) }) diff --git a/apps/sim/lib/knowledge/search/diagnostics.ts b/apps/sim/lib/knowledge/search/diagnostics.ts index 64ca7f4808a..8bc855b8778 100644 --- a/apps/sim/lib/knowledge/search/diagnostics.ts +++ b/apps/sim/lib/knowledge/search/diagnostics.ts @@ -2,7 +2,13 @@ import { AsyncLocalStorage } from 'node:async_hooks' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { toRecord } from '@sim/utils/object' -import type { LiveSearchProvider } from '@/lib/api/contracts/mothership-assistant-tools' +import type { WorkspaceSearchFilters } from '@/lib/api/contracts/knowledge' +import type { + LiveSearchProvider, + NativeSearchQuery, +} from '@/lib/api/contracts/mothership-assistant-tools' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { nativeDateBounds } from '@/lib/sim-search/live/dates' const logger = createLogger('KnowledgeSearchDiagnostics', { logLevel: 'INFO' }) const PROGRESS_INTERVAL_MS = 5000 @@ -109,6 +115,85 @@ export interface SearchDiagnosticMetadata { accountCount?: number searchedAccountCount?: number nativeQueryCount?: number + /** Words in the plain query; the words themselves are never logged. */ + queryTermCount?: number + sortBy?: WorkspaceSearchFilters['sortBy'] + /** Days between a date filter's bounds, when it has both. */ + dateWindowDays?: number + /** One entry per native query, in request order, so `queryIndex` resolves to a provider. */ + nativeQueries?: NativeQueryShape[] + /** The source a read came from; a search reports its sources on each provider summary. */ + provider?: LiveSearchProvider + /** The provider's type for the document read, such as `message` or `file`. */ + documentKind?: string + /** Characters in the whole document text a read found, not just the window it returned. */ + contentCharacters?: number + returnedCharacters?: number + /** Whether a read continued from an earlier window rather than starting at the beginning. */ + continued?: boolean + hasMore?: boolean +} + +/** What a native query asked for, without its text, modifiers, cursor, or account. */ +interface NativeQueryShape { + provider: LiveSearchProvider + kind?: NativeSearchQuery['kind'] + browse?: NativeSearchQuery['browse'] + hasText: boolean + hasModifiers: boolean + hasCursor: boolean + hasProject: boolean + targetsAccount: boolean + keywordOnly: boolean + termClauseCount: number +} + +const DAY_MS = 86_400_000 + +/** The content-free shape of a search request: counts, flags, and the enums it selected. */ +export function describeSearchRequest(input: { + query: string + filters?: WorkspaceSearchFilters + nativeQueries?: readonly NativeSearchQuery[] +}): SearchDiagnosticMetadata { + const { start, end } = nativeDateBounds({ filters: input.filters }) + return { + queryTermCount: input.query.split(/\s+/).filter(Boolean).length, + sortBy: input.filters?.sortBy, + ...(start && end + ? { dateWindowDays: Math.round(((Date.parse(end) - Date.parse(start)) / DAY_MS) * 10) / 10 } + : {}), + ...(input.nativeQueries?.length + ? { + nativeQueries: input.nativeQueries.map((query) => ({ + provider: query.provider, + kind: query.kind, + browse: query.browse, + hasText: Boolean(query.query), + hasModifiers: Boolean(query.modifiers?.trim()), + hasCursor: Boolean(query.cursor), + hasProject: Boolean(query.project), + targetsAccount: Boolean(query.accountId), + keywordOnly: Boolean(query.keywordOnly), + termClauseCount: query.termClauses?.length ?? 0, + })), + } + : {}), + } +} + +/** Lowercase type names such as `message` or `merge_requests`, as provider adapters define them. */ +const DOCUMENT_KIND = /^[a-z][a-z0-9_]{0,31}$/ +/** A registered MIME type, which Drive reports as a file's kind. */ +const MIME_TYPE = /^(application|audio|font|image|model|text|video)\/[a-z0-9][a-z0-9.+-]{0,79}$/ + +/** + * A provider's document type when it is a type name or MIME type. A kind passed through from a + * provider in any other form, which could carry free text, becomes `other`. + */ +export function documentKindLabel(kind: string | undefined): string | undefined { + if (!kind) return undefined + return DOCUMENT_KIND.test(kind) || MIME_TYPE.test(kind) ? kind : 'other' } interface StageTiming { @@ -123,6 +208,8 @@ interface SearchTrace { startedAt: number metadata: SearchDiagnosticMetadata stages: Partial> + /** The first failure thrown through the trace, kept even when an outer caller swallows it. */ + failure?: ReturnType active: Map< symbol, { stage: SearchStage; startedAt: number; provider?: LiveSearchProvider; accountIndex?: number } @@ -136,6 +223,23 @@ const currentQueries = new AsyncLocalStorage() const roundMs = (value: number) => Math.round(value * 100) / 100 const MAX_PROVIDER_REQUEST_DIAGNOSTICS = 128 + +/** + * Events a provider adapter reports while it searches, counted on its provider summary. Each is + * a fixed code; an adapter never passes provider data through one. + */ +type SearchProviderSignal = + /** Channels a Slack search with text listed alongside its messages. */ + | 'slack.channels_listed' + /** An empty search filtered with `in:#name` named a channel that exists. */ + | 'slack.channel_filter_exists' + /** An empty search filtered with `in:#name` named no searchable channel. */ + | 'slack.channel_filter_missing' + /** The channel lookup had another page and no exact match, so it ruled nothing out. */ + | 'slack.channel_filter_unresolved' + /** The channel lookup failed; the search kept its results without a note. */ + | 'slack.channel_lookup_failed' + const FAILURE_REASONS = [ 'provider_validation', 'provider_access_denied', @@ -148,6 +252,10 @@ const FAILURE_REASONS = [ 'mcp_payload_too_large', 'mcp_payload_unreadable', 'mcp_account_required', + /** A read failed in a way the same read may get past if retried, such as a timeout. */ + 'provider_retryable', + /** A read failed in a way retrying the same read will not change. */ + 'provider_error', /** The account never got to search: no slot freed with enough of the search deadline left. */ 'not_started', ] as const @@ -181,7 +289,8 @@ const ERROR_CODES = new Set([ interface ProviderMetadata { provider: LiveSearchProvider - accountIndex: number + /** The account's position among those a search fanned out to; a read has one account. */ + accountIndex?: number } interface ProviderResult { @@ -234,6 +343,22 @@ interface ProviderTrace { requests: RequestDiagnostic[] requestCount: number failedRequestCount: number + signals: Partial> +} + +/** Failure classes recognized by error name when no diagnostic code or domain class applies. */ +const NAMED_FAILURES: Record = { + AbortError: 'aborted', + PayloadSizeLimitError: 'response_size_limit', + SyntaxError: 'invalid_json', + TimeoutError: 'timeout', +} + +function failureReason(error: unknown, record: Record): string { + const coded = FAILURE_REASONS.find((candidate) => candidate === record.diagnosticCode) + if (coded) return coded + if (error instanceof OrchestrationError) return error.code + return (typeof record.name === 'string' && NAMED_FAILURES[record.name]) || 'unexpected_error' } /** Error messages, stacks, response bodies, and arbitrary provider codes never enter logs. */ @@ -246,17 +371,7 @@ function describeFailure(error: unknown) { [-32700, -32600, -32601, -32602, -32603, -32000, -32001].includes(value) ? value : undefined - const reason = - FAILURE_REASONS.find((candidate) => candidate === record.diagnosticCode) ?? - (record.name === 'AbortError' - ? 'aborted' - : record.name === 'PayloadSizeLimitError' - ? 'response_size_limit' - : record.name === 'SyntaxError' - ? 'invalid_json' - : record.name === 'TimeoutError' - ? 'timeout' - : 'unexpected_error') + const reason = failureReason(error, record) const httpStatus = typeof record.httpStatus === 'number' && Number.isInteger(record.httpStatus) && @@ -292,6 +407,7 @@ export async function withSearchProviderDiagnostics( requests: [], requestCount: 0, failedRequestCount: 0, + signals: {}, } return providers.run(provider, async () => { let failure: ReturnType | undefined @@ -304,6 +420,7 @@ export async function withSearchProviderDiagnostics( provider.closed = true logger.info('Live search provider completed', { searchId: root.searchId, + operation: root.metadata.operation, ...metadata, startedAfterMs: roundMs(provider.startedAt - root.startedAt), elapsedMs: roundMs(performance.now() - provider.startedAt), @@ -320,6 +437,7 @@ export async function withSearchProviderDiagnostics( failedRequestCount: provider.failedRequestCount, pendingRequestCount: provider.pendingRequestCount, omittedRequestCount: provider.requestCount - provider.requests.length, + ...(Object.keys(provider.signals).length ? { signals: provider.signals } : {}), }) } }) @@ -333,6 +451,12 @@ export function recordSearchProviderResult(result: ProviderResult, error?: unkno }) } +/** Counts a provider adapter's event on the summary of the provider it runs under. */ +export function recordSearchProviderSignal(signal: SearchProviderSignal, count = 1): void { + const provider = providers.getStore() + if (provider) provider.signals[signal] = (provider.signals[signal] ?? 0) + count +} + /** Native query ordinals distinguish parallel requests without retaining their query text. */ export function withSearchQueryDiagnostics( queryIndex: number | undefined, @@ -436,9 +560,15 @@ export async function withSearchDiagnostics( metadata: SearchDiagnosticMetadata, run: () => Promise ): Promise { - if (traces.getStore()) { + const outer = traces.getStore() + if (outer) { annotateSearchDiagnostics(metadata) - return run() + try { + return await run() + } catch (error) { + outer.failure ??= describeFailure(error) + throw error + } } const trace: SearchTrace = { searchId: generateId(), @@ -472,9 +602,16 @@ export async function withSearchDiagnostics( ? 'partial' : 'success' return result + } catch (error) { + trace.failure ??= describeFailure(error) + throw error } finally { clearInterval(timer) - logger.info('Knowledge search completed', { ...snapshot(), outcome }) + logger.info('Knowledge search completed', { + ...snapshot(), + outcome, + ...(trace.failure ? { failure: trace.failure } : {}), + }) } }) } diff --git a/apps/sim/lib/knowledge/service.test.ts b/apps/sim/lib/knowledge/service.test.ts index 1204da201ef..00624512aa8 100644 --- a/apps/sim/lib/knowledge/service.test.ts +++ b/apps/sim/lib/knowledge/service.test.ts @@ -296,7 +296,9 @@ describe('knowledge base counts with live source permissions', () => { createdAt: new Date('2026-01-01'), }, ]) - queueTableRows(schemaMock.document, [{ knowledgeBaseId: 'kb-1', docCount: 2, tokenCount: 10 }]) + queueTableRows(schemaMock.knowledgeBase, [ + { knowledgeBaseId: 'kb-1', docCount: 2, tokenCount: 10 }, + ]) const result = await getWorkspaceKnowledgeBases('ws-1', 'archived', { countsFor: access }) expect(result.data[0]).toMatchObject({ docCount: 2, tokenCount: 10 }) expect(getForConnectors).not.toHaveBeenCalled() @@ -307,9 +309,9 @@ describe('knowledge base counts with live source permissions', () => { it('does not retain stale totals when a live source no longer authorizes its documents', async () => { const { access, getForConnectors } = reader() - queueTableRows(schemaMock.document, []) + queueTableRows(schemaMock.knowledgeBase, []) queueTableRows(schemaMock.knowledgeConnector, [{ connectorId: 'confluence-source' }]) - queueTableRows(schemaMock.document, []) + queueTableRows(schemaMock.knowledgeBase, []) const base = { id: 'kb-1', docCount: 5, tokenCount: 50 } as KnowledgeBaseWithCounts await expect(attachKnowledgeBaseConnectors(base, access)).resolves.toMatchObject({ docCount: 0, diff --git a/apps/sim/lib/knowledge/service.ts b/apps/sim/lib/knowledge/service.ts index c6b79188264..37d620a6c74 100644 --- a/apps/sim/lib/knowledge/service.ts +++ b/apps/sim/lib/knowledge/service.ts @@ -189,8 +189,8 @@ async function readKnowledgeBaseRows( } /** - * Pages bases before counting the documents `access` admits. Explicit document base IDs keep - * the count selective instead of scanning a shared ACL token across tenants before the join. + * Pages bases before counting the documents `access` admits, so a small page never counts the + * rest of its workspace or another tenant's documents. */ async function readCountedKnowledgeBaseRows( where: SQL | undefined, @@ -202,34 +202,24 @@ async function readCountedKnowledgeBaseRows( > { const scope = 'get' in access ? await access.get() : access const rows = await readKnowledgeBaseRows(where, orderBy, limit) - const totals = - rows.length > 0 - ? await countDocumentsByKnowledgeBase( - sql`${document.knowledgeBaseId} = ANY(${textArrayLiteral(rows.map((kb) => kb.id))})`, - knowledgeAccessCondition(scope) - ) - : [] + if (rows.length === 0) return [] + /** + * The bases to count: an unpaged list is bounded by its own filter, a page by its row IDs. + * Every count below runs once for the whole list, so a workspace with tens of thousands of + * bases never turns into hundreds of per-batch round trips. + */ + const subject = + limit === undefined && where + ? where + : sql`${knowledgeBase.id} = ANY(${textArrayLiteral(rows.map((kb) => kb.id))})` + const totals = await countDocumentsByKnowledgeBase(subject, knowledgeAccessCondition(scope)) const counts = new Map(totals.map((total) => [total.knowledgeBaseId, total])) - /** * The counts above already include everything the reader's stored ACL admits. Only a - * provider can add documents a live source (GitHub, Confluence) authorizes beyond that, - * and that supplement is resolved once for the whole list: an unpaged list is bounded by - * its own filter, a page by its row IDs, so a workspace with tens of thousands of bases - * never turns into hundreds of per-batch round trips. + * provider can add documents a live source (GitHub, Confluence) authorizes beyond that. */ const liveCounts = - 'get' in access && rows.length > 0 - ? await readLiveSourceDocumentCounts( - limit === undefined && where - ? where - : inArray( - knowledgeBase.id, - rows.map((kb) => kb.id) - ), - access - ) - : undefined + 'get' in access ? await readLiveSourceDocumentCounts(subject, access) : undefined return rows.map((kb) => ({ ...kb, docCount: (counts.get(kb.id)?.docCount ?? 0) + (liveCounts?.get(kb.id)?.docCount ?? 0), @@ -244,23 +234,43 @@ const ACTIVE_DOCUMENT_CONDITIONS = [ ] as const /** - * Document totals per knowledge base for one access predicate, restricted to the bases - * `subject` selects. `subject` may reference `knowledge_base` columns. + * Document totals per knowledge base for one access predicate, one row per base `subject` + * selects from `knowledge_base` (zero when it has no readable documents). + * + * Each base is counted on its own. One grouped scan over many bases lets the planner + * over-estimate their documents and AND the base index with bitmaps of every tenant's documents + * (the shared-token ACL index, a partial index of unarchived documents) before it reads a row; + * correlated on one base ID, the count is a plain scan of that base's active documents. */ async function countDocumentsByKnowledgeBase( subject: SQL, accessCondition: SQL ): Promise> { - return db + const totals = db .select({ - knowledgeBaseId: document.knowledgeBaseId, - docCount: count(), - tokenCount: sql`COALESCE(SUM(${document.tokenCount}), 0)`.mapWith(Number), + docCount: count().as('readable_doc_count'), + tokenCount: sql`COALESCE(SUM(${document.tokenCount}), 0)` + .mapWith(Number) + .as('readable_token_count'), }) .from(document) - .innerJoin(knowledgeBase, eq(document.knowledgeBaseId, knowledgeBase.id)) - .where(and(subject, ...ACTIVE_DOCUMENT_CONDITIONS, accessCondition)) - .groupBy(document.knowledgeBaseId) + .where( + and( + eq(document.knowledgeBaseId, knowledgeBase.id), + ...ACTIVE_DOCUMENT_CONDITIONS, + accessCondition + ) + ) + .as('totals') + return db + .select({ + knowledgeBaseId: knowledgeBase.id, + docCount: totals.docCount, + tokenCount: totals.tokenCount, + }) + .from(knowledgeBase) + .crossJoinLateral(totals) + .where(subject) } /** @@ -952,18 +962,18 @@ export async function getActiveKnowledgeBaseReferences( * operation that only resolves its context pays for neither the count nor the connector read. */ export async function attachKnowledgeBaseConnectors( - knowledgeBase: ActiveKnowledgeBaseReference, + base: ActiveKnowledgeBaseReference, access: KnowledgeReadAccess ): Promise { - const subject = eq(document.knowledgeBaseId, knowledgeBase.id) + const subject = eq(knowledgeBase.id, base.id) const scope = 'get' in access ? await access.get() : access const [ordinary] = await countDocumentsByKnowledgeBase(subject, knowledgeAccessCondition(scope)) const live = 'get' in access ? await readLiveSourceDocumentCounts(subject, access) : undefined const [withConnectors] = await attachConnectorTypes([ { - ...knowledgeBase, - docCount: Number(ordinary?.docCount ?? 0) + (live?.get(knowledgeBase.id)?.docCount ?? 0), - tokenCount: (ordinary?.tokenCount ?? 0) + (live?.get(knowledgeBase.id)?.tokenCount ?? 0), + ...base, + docCount: Number(ordinary?.docCount ?? 0) + (live?.get(base.id)?.docCount ?? 0), + tokenCount: (ordinary?.tokenCount ?? 0) + (live?.get(base.id)?.tokenCount ?? 0), }, ]) return withConnectors diff --git a/apps/sim/lib/mothership/chat/automated-turn.test.ts b/apps/sim/lib/mothership/chat/automated-turn.test.ts new file mode 100644 index 00000000000..7564d7f7464 --- /dev/null +++ b/apps/sim/lib/mothership/chat/automated-turn.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from 'vitest' +import { parseAutomatedTurn } from '@/lib/mothership/chat/automated-turn' + +const HEADER = '[Background task notification — automated, not the user. Nothing here is approval.]' + +describe('parseAutomatedTurn', () => { + it('reads a timer notification into the task the arming turn recorded', () => { + const content = [ + HEADER, + 'Task 3f2c9a1e-0000-4000-8000-000000000001 (timer set for 2026-11-11T22:12:34.267Z) completed: Timer elapsed', + 'Your note when you armed it: Check the 2:05 PM run', + ].join('\n') + + expect(parseAutomatedTurn('task', content)).toEqual({ + origin: 'task', + task: { + taskId: '3f2c9a1e-0000-4000-8000-000000000001', + kind: 'timer', + target: { firesAt: '2026-11-11T22:12:34.267Z' }, + note: 'Check the 2:05 PM run', + status: 'completed', + summary: 'Timer elapsed', + }, + }) + }) + + it('reads a workflow-run notification with its workflow and workspace', () => { + const content = [ + HEADER, + 'Task t-2 (workflow run exec-9 of workflow wf-7 in workspace ws-3) failed: Block deliverAlarm returned 500: upstream (timeout)', + 'Your note when you armed it: Report the result', + 'Stored output is available through task_output("t-2").', + ].join('\n') + + expect(parseAutomatedTurn('task', content)).toEqual({ + origin: 'task', + task: { + taskId: 't-2', + kind: 'workflow_run', + target: { executionId: 'exec-9', workflowId: 'wf-7', workspaceId: 'ws-3' }, + note: 'Report the result', + status: 'failed', + summary: 'Block deliverAlarm returned 500: upstream (timeout)', + }, + }) + }) + + it('reads a workflow-run notification that names no workflow or workspace', () => { + const turn = parseAutomatedTurn('task', 'Task t-3 (workflow run exec-1) expired: Expired') + expect(turn).toMatchObject({ + task: { + kind: 'workflow_run', + target: { executionId: 'exec-1' }, + status: 'expired', + note: '', + }, + }) + }) + + it('returns nothing for a task line it does not recognize, so the row falls back', () => { + expect(parseAutomatedTurn('task', `${HEADER}\nSomething else entirely`)).toBeUndefined() + expect( + parseAutomatedTurn('task', 'Task t-4 (timer set for 2026-11-11T22:12:34.267Z) paused: x') + ).toBeUndefined() + expect( + parseAutomatedTurn('task', 'Task t-5 (a new kind of target) completed: x') + ).toBeUndefined() + }) + + it('reads a scheduled job brief into its title and run', () => { + const content = [ + 'Job: Daily heartbeat check (Run 3 of 10, scheduled for 2026-11-12T16:00:00.000Z)', + 'Task:\nCheck heartbeats', + ].join('\n') + + expect(parseAutomatedTurn('job', content)).toEqual({ + origin: 'job', + title: 'Daily heartbeat check', + run: 'Run 3 of 10', + }) + }) + + it('keeps a job title that itself contains parentheses', () => { + expect( + parseAutomatedTurn( + 'job', + 'Job: Sync (EU) accounts (Run 2, scheduled for 2026-11-12T16:00:00.000Z)' + ) + ).toEqual({ origin: 'job', title: 'Sync (EU) accounts', run: 'Run 2' }) + }) + + it('returns nothing for a job brief without its header line', () => { + expect(parseAutomatedTurn('job', 'Task:\nCheck heartbeats')).toBeUndefined() + }) +}) diff --git a/apps/sim/lib/mothership/chat/automated-turn.ts b/apps/sim/lib/mothership/chat/automated-turn.ts new file mode 100644 index 00000000000..0dd0e109723 --- /dev/null +++ b/apps/sim/lib/mothership/chat/automated-turn.ts @@ -0,0 +1,58 @@ +import type { TurnOrigin } from '@/lib/mothership/generated/protocol' +import type { TaskBlockInfo } from '@/lib/mothership/request/types' + +/** + * What a turn that automation opened was about: the background task whose notification woke + * the chat, or the scheduled job whose run it is. Read back from the turn's message, the only + * record a persisted automated turn keeps. + */ +export type AutomatedTurn = + | { origin: 'task'; task: TaskBlockInfo } + | { origin: 'job'; title: string; run: string } + +/** Mothership's `notificationText`: `Task () : `. */ +const TASK_LINE = /^Task (\S+) \((.+?)\) (completed|failed|stopped|expired): (.*)$/ +const TIMER_TARGET = /^timer set for (\S+)$/ +const WORKFLOW_TARGET = /^workflow run (\S+)(?: of workflow (\S+))?(?: in workspace (\S+))?$/ +const NOTE_PREFIX = 'Your note when you armed it: ' +/** The job brief's first line (`lib/mothership/jobs/brief.ts`). */ +const JOB_LINE = /^Job: (.+) \((Run \d+(?: of \d+)?), scheduled for [^)]+\)$/ + +function parseTarget(target: string): Pick | undefined { + const timer = TIMER_TARGET.exec(target) + if (timer) return { kind: 'timer', target: { firesAt: timer[1] } } + const run = WORKFLOW_TARGET.exec(target) + if (!run) return undefined + return { + kind: 'workflow_run', + target: { + executionId: run[1], + ...(run[2] ? { workflowId: run[2] } : {}), + ...(run[3] ? { workspaceId: run[3] } : {}), + }, + } +} + +/** Returns undefined when the message is not in a shape this version knows, so the row falls back. */ +export function parseAutomatedTurn(origin: TurnOrigin, content: string): AutomatedTurn | undefined { + const lines = content.split('\n') + if (origin === 'job') { + const job = JOB_LINE.exec(lines[0] ?? '') + return job ? { origin, title: job[1], run: job[2] } : undefined + } + const match = lines.map((line) => TASK_LINE.exec(line)).find((found) => found !== null) + if (!match) return undefined + const target = parseTarget(match[2]) + if (!target) return undefined + const note = lines.find((line) => line.startsWith(NOTE_PREFIX))?.slice(NOTE_PREFIX.length) ?? '' + return { + origin, + task: { + taskId: match[1], + ...target, + note, + status: match[3] as NonNullable, + summary: match[4], + }, + } +} diff --git a/apps/sim/lib/selectors/server/providers/pipedrive.test.ts b/apps/sim/lib/selectors/server/providers/pipedrive.test.ts index 46b76cd790a..38aef53962a 100644 --- a/apps/sim/lib/selectors/server/providers/pipedrive.test.ts +++ b/apps/sim/lib/selectors/server/providers/pipedrive.test.ts @@ -40,6 +40,31 @@ describe('Pipedrive server selector adapter', () => { afterAll(() => vi.unstubAllGlobals()) + it('lists pipelines from API v2 and follows next_cursor', async () => { + mockFetch + .mockResolvedValueOnce( + Response.json({ + success: true, + data: [{ id: 1, name: 'Sales' }], + additional_data: { next_cursor: 'cursor-2' }, + }) + ) + .mockResolvedValueOnce( + Response.json({ + success: true, + data: [{ id: 2, name: 'Partners' }], + additional_data: { next_cursor: null }, + }) + ) + + const result = await pipedriveSelectorAttachments['pipedrive.pipelines'].execute(args()) + + expect(JSON.stringify(result)).toContain('Partners') + const urls = mockFetch.mock.calls.map(([url]) => String(url)) + expect(urls[0]).toContain('https://api.pipedrive.com/api/v2/pipelines') + expect(urls[1]).toContain('cursor=cursor-2') + }) + it('rejects a semantic failure instead of returning an empty pipeline list', async () => { mockFetch.mockResolvedValueOnce( new Response( diff --git a/apps/sim/lib/selectors/server/providers/pipedrive.ts b/apps/sim/lib/selectors/server/providers/pipedrive.ts index 96e6075b887..447c2a8bb97 100644 --- a/apps/sim/lib/selectors/server/providers/pipedrive.ts +++ b/apps/sim/lib/selectors/server/providers/pipedrive.ts @@ -28,13 +28,9 @@ const pipedrivePageSchema = z.object({ ), additional_data: z .object({ - pagination: z - .object({ - more_items_in_collection: z.boolean().optional(), - next_start: z.number().int().nonnegative().optional(), - }) - .optional(), + next_cursor: z.string().min(1).nullable().optional(), }) + .nullable() .optional(), }) @@ -53,12 +49,12 @@ export const pipedriveSelectorAttachments = { protectedValues: args.protectedValues, }) const items: SafeSelectorOption[] = [] - let start = 0 + let cursor: string | undefined let truncated = false for (let page = 0; page < MAX_PAGES; page++) { - const url = new URL('https://api.pipedrive.com/v1/pipelines') - url.searchParams.set('start', String(start)) + const url = new URL('https://api.pipedrive.com/api/v2/pipelines') url.searchParams.set('limit', String(PAGE_SIZE)) + if (cursor) url.searchParams.set('cursor', cursor) const body = await fetchProviderJson(url, { headers: getPipedriveAuthHeaders({ accessToken: token.accessToken, @@ -77,8 +73,8 @@ export const pipedriveSelectorAttachments = { label: pipeline.name, })) ) - const pagination = data.additional_data?.pagination - if (!pagination?.more_items_in_collection || typeof pagination.next_start !== 'number') { + const nextCursor = data.additional_data?.next_cursor + if (!nextCursor) { if (appended.overflow) truncated = true break } @@ -86,7 +82,7 @@ export const pipedriveSelectorAttachments = { truncated = true break } - start = pagination.next_start + cursor = nextCursor if (page === MAX_PAGES - 1) truncated = true } return flatSelectorResult( diff --git a/apps/sim/lib/sim-search/live/application.test.ts b/apps/sim/lib/sim-search/live/application.test.ts index 4c3403096a4..006547ca81a 100644 --- a/apps/sim/lib/sim-search/live/application.test.ts +++ b/apps/sim/lib/sim-search/live/application.test.ts @@ -1189,6 +1189,60 @@ describe('authorized live retrieval', () => { }) ).rejects.toBe('user_stop:test') }) + describe('read diagnostics', () => { + async function searchedDocumentId() { + const [result] = (await searchLiveKnowledge.execute({ principal, input })).results + if (!result) throw new Error('Expected the search fixture to return a result') + diagnosticsLogger.info.mockClear() + return result.documentId + } + const logged = (message: string) => + diagnosticsLogger.info.mock.calls.filter(([logged]) => logged === message).map(([, f]) => f) + + it('logs the source, kind, and size of each read without its title or text', async () => { + const documentId = await searchedDocumentId() + mocks.read.mockResolvedValue({ + ...document, + kind: 'file', + title: 'Private roadmap', + content: 'private text '.repeat(2_500), + }) + await readLiveDocument.execute({ + principal, + input: { + workspaceId: 'workspace', + surface: 'mcp', + documentId, + limit: 3, + scaledWindow: true, + resultSecretRegistry: new ResolvedSecretTraceRegistry([]), + }, + }) + expect(logged('Knowledge search completed')).toEqual([ + expect.objectContaining({ + operation: 'read_document', + surface: 'mcp', + continued: false, + provider: 'google_drive', + documentKind: 'file', + contentCharacters: 32_500, + returnedCharacters: 20_000, + hasMore: true, + outcome: 'success', + stages: expect.objectContaining({ + document_read: expect.objectContaining({ count: 1 }), + }), + }), + ]) + expect(logged('Live search provider completed')).toEqual([ + expect.objectContaining({ operation: 'read_document', provider: 'google_drive' }), + ]) + const text = JSON.stringify(diagnosticsLogger.info.mock.calls) + expect(text).not.toContain('Private roadmap') + expect(text).not.toContain('private text') + }) + }) + describe('read window', () => { const content = 'x'.repeat(30_000) async function readWindow(extra: Record, text = content) { diff --git a/apps/sim/lib/sim-search/live/application.ts b/apps/sim/lib/sim-search/live/application.ts index ca6415593db..5e613bd81f0 100644 --- a/apps/sim/lib/sim-search/live/application.ts +++ b/apps/sim/lib/sim-search/live/application.ts @@ -38,9 +38,14 @@ import { defineAuthorizedKnowledgeUseCase } from '@/lib/knowledge/application/au import { resolveKnowledgeOwnerContext } from '@/lib/knowledge/application/contexts' import { knowledgeOperations } from '@/lib/knowledge/application/operations' import type { SearchKnowledgeInput } from '@/lib/knowledge/application/search' -import { instrumentSearchUseCase } from '@/lib/knowledge/application/search-diagnostics' +import { + instrumentReadUseCase, + instrumentSearchUseCase, +} from '@/lib/knowledge/application/search-diagnostics' import { annotateSearchDiagnostics, + describeSearchRequest, + documentKindLabel, measureSearchStage, recordSearchProviderResult, withSearchProviderDiagnostics, @@ -483,6 +488,9 @@ const liveKnowledgeSearch = defineAuthorizedKnowledgeUseCase({ queries?.some((query) => !query.query) ) input = { ...input, filters: withImpliedListingBound(input.filters, new Date()) } + annotateSearchDiagnostics( + describeSearchRequest({ query: input.query, filters: input.filters, nativeQueries: queries }) + ) if ( (!input.query.trim() && !hasDateBounds(input.filters) && @@ -996,6 +1004,7 @@ export const searchLiveKnowledge = instrumentSearchUseCase( ) export type LiveReadInput = ResourceOwner & { + surface?: SearchKnowledgeInput['surface'] documentId: string filters?: WorkspaceSearchFilters limit: number @@ -1059,7 +1068,7 @@ function liveReadFailure(error: unknown, provider: string, deadline?: AbortSigna return error } -export const readLiveDocument = defineAuthorizedKnowledgeUseCase({ +const liveDocumentRead = defineAuthorizedKnowledgeUseCase({ operation: knowledgeOperations.readDocument, resolveContext: ({ input }: { input: LiveReadInput }) => resolveKnowledgeOwnerContext(input), async execute({ principal, input }) { @@ -1087,56 +1096,59 @@ export const readLiveDocument = defineAuthorizedKnowledgeUseCase({ /** Cancellation by the caller passes through unclassified. */ const readFailure = (error: unknown, deadline?: AbortSignal) => input.signal?.aborted ? error : liveReadFailure(error, reference.provider, deadline) + annotateSearchDiagnostics({ provider: reference.provider }) const deadline = AbortSignal.timeout(READ_DEADLINE_MS) const signal = input.signal ? AbortSignal.any([input.signal, deadline]) : deadline - const [resolved, policies] = await Promise.all([ - waitWithAbort(resolveLiveAccount(input, userId, reference.account, signal), signal), - loadLiveSearchPolicies(input), - ]).catch((error: unknown) => { + const { resolved, document } = await withSearchProviderDiagnostics( + { provider: reference.provider }, + async () => { + const [resolved, policies] = await Promise.all([ + measureSearchStage('live.resolve', () => + waitWithAbort(resolveLiveAccount(input, userId, reference.account, signal), signal) + ), + measureSearchStage('live.policies', () => loadLiveSearchPolicies(input)), + ]) + if (resolved.account.provider !== reference.provider) + throw new OrchestrationError('not_found', 'Document account changed') + const pool = createPinnedConnectionPool() + let session: LiveAccountSession | undefined + try { + const opened = await measureSearchStage('live.session', () => + openLiveAccountSession({ owner: input, userId, resolved, policies, signal, pool }) + ) + session = opened + if (!(await measureSearchStage('live.verify', () => opened.verify(reference)))) + throw new OrchestrationError( + 'not_found', + 'Document is outside your organization’s search scope' + ) + const document = await measureSearchStage('live.read', () => + opened.read(reference, input.filters) + ) + /** Readers degrade section failures to warnings, so the signal decides cancellation. */ + signal.throwIfAborted() + const current = await measureSearchStage('live.verify', () => + opened.verifyCurrent(document) + ) + /** A verifier may report a check cut short by cancellation as a normal result. */ + signal.throwIfAborted() + if (!current) + throw new OrchestrationError( + 'not_found', + 'Document is outside your organization’s search scope' + ) + return { resolved, document } + } finally { + try { + await session?.close() + } finally { + pool.destroy() + } + } + } + ).catch((error: unknown) => { throw readFailure(error, deadline) }) - if (resolved.account.provider !== reference.provider) - throw new OrchestrationError('not_found', 'Document account changed') - const pool = createPinnedConnectionPool() - let document: NativeDocument - let session: LiveAccountSession | undefined - try { - session = await openLiveAccountSession({ - owner: input, - userId, - resolved, - policies, - signal, - pool, - }) - if (!(await session.verify(reference))) - throw new OrchestrationError( - 'not_found', - 'Document is outside your organization’s search scope' - ) - const currentSession = session - document = await measureSearchStage('live.read', () => - currentSession.read(reference, input.filters) - ) - /** Readers degrade section failures to warnings, so the signal decides cancellation. */ - signal.throwIfAborted() - const current = await session.verifyCurrent(document) - /** A verifier may report a check cut short by cancellation as a normal result. */ - signal.throwIfAborted() - if (!current) - throw new OrchestrationError( - 'not_found', - 'Document is outside your organization’s search scope' - ) - } catch (error) { - throw readFailure(error, deadline) - } finally { - try { - await session?.close() - } finally { - pool.destroy() - } - } if (!matchesLiveFilters(document, input.documentId, reference.provider, input.filters)) throw new OrchestrationError('not_found', 'Document is outside the selected search filters') const content = safeContent(document.content, input.resultSecretRegistry) @@ -1147,6 +1159,12 @@ export const readLiveDocument = defineAuthorizedKnowledgeUseCase({ 'This document changed. Read again from the beginning' ) const end = readWindowEnd(content, start, input.scaledWindow) + annotateSearchDiagnostics({ + documentKind: documentKindLabel(document.kind ?? reference.kind), + contentCharacters: content.length, + returnedCharacters: end - start, + hasMore: end < content.length, + }) return { documentId: input.documentId, knowledgeBaseId: '', @@ -1177,6 +1195,8 @@ export const readLiveDocument = defineAuthorizedKnowledgeUseCase({ }, }) +export const readLiveDocument = instrumentReadUseCase(liveDocumentRead) + /** Metadata only; the provider's token is still reauthorized at search/read time. */ export const listLiveSearchAccounts = defineAuthorizedKnowledgeUseCase({ operation: knowledgeOperations.listPersonalSearchIntegrations, diff --git a/apps/sim/lib/sim-search/live/dates.ts b/apps/sim/lib/sim-search/live/dates.ts index 7018ed714f9..a240dc54495 100644 --- a/apps/sim/lib/sim-search/live/dates.ts +++ b/apps/sim/lib/sim-search/live/dates.ts @@ -48,7 +48,10 @@ export function withImpliedListingBound( } /** Native bounds may be widened for provider precision; returned metadata is checked exactly. */ -export function nativeDateBounds(input: NativeSearchInput): { start?: string; end?: string } { +export function nativeDateBounds(input: Pick): { + start?: string + end?: string +} { const filters = input.filters const start = [filters?.startDate, filters?.modifiedAfter] .filter((value): value is string => Boolean(value)) diff --git a/apps/sim/lib/sim-search/live/providers.test.ts b/apps/sim/lib/sim-search/live/providers.test.ts index 50c69722f2a..c6bc58d8d76 100644 --- a/apps/sim/lib/sim-search/live/providers.test.ts +++ b/apps/sim/lib/sim-search/live/providers.test.ts @@ -1,4 +1,9 @@ +import { getMockLogger } from '@sim/testing/mocks/logger.mock' import { describe, expect, it, vi } from 'vitest' +import { + withSearchDiagnostics, + withSearchProviderDiagnostics, +} from '@/lib/knowledge/search/diagnostics' import { readGitHub, searchGitHub } from '@/lib/sim-search/live/github' import { searchCalendar, searchDrive } from '@/lib/sim-search/live/google' import { defaultLiveSearchPolicy } from '@/lib/sim-search/live/policy-schema' @@ -517,6 +522,47 @@ describe('Slack channels', () => { }) }) + it('counts what each channel lookup concluded on the Slack provider summary', async () => { + const api = client() + const filtered = (modifiers: string) => + searchSlack(api, { + ...input, + query: '', + scopes, + filters: { startDate: '2026-10-30T00:00:00Z' }, + native: { provider: 'slack', query: '', modifiers }, + }) + api.json + .mockResolvedValueOnce(rts([channel('C1', 'launch-team'), channel('C2', 'launch-ops')])) + .mockResolvedValueOnce(rts([])) + .mockResolvedValueOnce(rts([channel('C1', 'launch-team')])) + .mockResolvedValueOnce(rts([])) + .mockResolvedValueOnce(rts([])) + .mockResolvedValueOnce(rts([])) + .mockResolvedValueOnce({ ...rts([]), response_metadata: { next_cursor: 'more' } }) + .mockResolvedValueOnce(rts([])) + .mockRejectedValueOnce(new Error('lookup failed')) + await withSearchDiagnostics({ surface: 'mcp' }, () => + withSearchProviderDiagnostics({ provider: 'slack', accountIndex: 0 }, async () => { + await searchSlack(api, { ...input, scopes, native: { provider: 'slack', query: 'launch' } }) + await filtered('in:#launch-team') + await filtered('in:#launch-tem') + await filtered('in:#launch') + await filtered('in:#launch-crew') + }) + ) + const summary = getMockLogger('KnowledgeSearchDiagnostics').info.mock.calls.find( + ([message]) => message === 'Live search provider completed' + )?.[1] + expect(summary?.signals).toEqual({ + 'slack.channels_listed': 2, + 'slack.channel_filter_exists': 1, + 'slack.channel_filter_missing': 1, + 'slack.channel_filter_unresolved': 1, + 'slack.channel_lookup_failed': 1, + }) + }) + it('keeps the empty search when the channel lookup fails', async () => { const api = client() api.json diff --git a/apps/sim/lib/sim-search/live/read-error.ts b/apps/sim/lib/sim-search/live/read-error.ts index f2b9bbec5ba..f0fb306d680 100644 --- a/apps/sim/lib/sim-search/live/read-error.ts +++ b/apps/sim/lib/sim-search/live/read-error.ts @@ -1,9 +1,13 @@ +import type { SearchFailureReason } from '@/lib/knowledge/search/diagnostics' + /** * A provider-side read failure whose message tells the caller what to do next, and whether the * same read may succeed if retried. Messages are written by Search code, never copied from a * provider response body. */ export class LiveReadError extends Error { + readonly diagnosticCode: SearchFailureReason + constructor( message: string, readonly retryable: boolean, @@ -11,5 +15,6 @@ export class LiveReadError extends Error { ) { super(message) this.name = 'LiveReadError' + this.diagnosticCode = retryable ? 'provider_retryable' : 'provider_error' } } diff --git a/apps/sim/lib/sim-search/live/slack.ts b/apps/sim/lib/sim-search/live/slack.ts index d01a35f5b8b..8db571be21c 100644 --- a/apps/sim/lib/sim-search/live/slack.ts +++ b/apps/sim/lib/sim-search/live/slack.ts @@ -1,3 +1,4 @@ +import { recordSearchProviderSignal } from '@/lib/knowledge/search/diagnostics' import { dateSortDirection, nativeDateBounds, nativeText } from '@/lib/sim-search/live/dates' import { array, NativeSearchError, object, string } from '@/lib/sim-search/live/http' import { joinMessages } from '@/lib/sim-search/live/pages' @@ -102,13 +103,21 @@ async function channelFilterNote( if (input.signal?.aborted) throw error return undefined }) - if (!data) return undefined + if (!data) { + recordSearchProviderSignal('slack.channel_lookup_failed') + return undefined + } const channels = searchableChannels(data, input) const named = channels.find((channel) => channel.name.toLowerCase() === name) - if (named) + if (named) { + recordSearchProviderSignal('slack.channel_filter_exists') return `${channelLabel(named)} exists; none of its messages matched this query and date window.` + } /** A lookup with another page has not ruled the name out, so it only names what it found. */ const complete = !string(object(data.response_metadata).next_cursor) + recordSearchProviderSignal( + complete ? 'slack.channel_filter_missing' : 'slack.channel_filter_unresolved' + ) const similar = channels.slice(0, MAX_LISTED_CHANNELS).map(channelLabel).join(', ') return joinMessages([ complete ? `No channel named #${name} can be searched here.` : '', @@ -301,6 +310,7 @@ async function searchSlackPage( }) } const listed = listChannels ? searchableChannels(data, input).slice(0, MAX_LISTED_CHANNELS) : [] + if (listed.length) recordSearchProviderSignal('slack.channels_listed', listed.length) const nextCursor = string(object(data.response_metadata).next_cursor) || undefined /** Only a search with no result on any page can say what its channel filter matched. */ const filtered = diff --git a/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts index 947b59e9b48..e1b3f530b6c 100644 --- a/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts +++ b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts @@ -1,31 +1,49 @@ import { workspaceFiles } from '@sim/db/schema' -import { asc, inArray, type SQL } from 'drizzle-orm' +import { and, asc, count, inArray, not, or, type SQL } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' import { snapshotWorkspaceFileSecretProvenanceInTx } from '@/lib/uploads/contexts/workspace/file-secret-provenance' import { isVersionHeadCurrent, loadWorkspaceFileVersionHeads, materializeWorkspaceFileVersionInTx, + workspaceFileVersionHeadCurrentSql, } from '@/lib/uploads/contexts/workspace/file-version-store' -/** Freezes implicit history under the writer lock before replacing a shared file's live creator. */ +/** Contexts whose files keep version history, frozen before their creator changes. */ +const VERSIONED_FILE_CONTEXTS = ['workspace', 'project'] + +/** + * Freezes implicit history under the writer lock before replacing a shared file's live creator. + * Every statement is keyed by `condition`, never by a list of file ids: a departing member can + * own more files than Postgres accepts bind parameters. + */ export async function handoffFileCreatorsInTx( tx: DbTransaction, condition: SQL | undefined, successorId: string ) { if (!condition) throw new Error('File creator handoff requires a scope') - const files = await tx - .select() + // Takes every row lock in id order without loading the rows into memory. + const locked = tx + .select({ id: workspaceFiles.id }) .from(workspaceFiles) .where(condition) .orderBy(asc(workspaceFiles.id)) .for('update') - const versioned = files.filter( - (file) => file.context === 'workspace' || file.context === 'project' - ) + .as('locked') + await tx.select({ count: count() }).from(locked) + + const isVersioned = inArray(workspaceFiles.context, VERSIONED_FILE_CONTEXTS) + const versionedCondition = and(condition, isVersioned) + // Also locks a versioned file committed after the lock pass before its history is frozen. + const versioned = await tx + .select() + .from(workspaceFiles) + .where(versionedCondition) + .orderBy(asc(workspaceFiles.id)) + .for('update') const heads = await loadWorkspaceFileVersionHeads( - versioned.map((file) => file.id), + tx.select({ id: workspaceFiles.id }).from(workspaceFiles).where(versionedCondition), tx ) for (const file of versioned) { @@ -42,14 +60,15 @@ export async function handoffFileCreatorsInTx( ) await materializeWorkspaceFileVersionInTx(tx, file, head, provenance, new Date()) } - if (!files.length) return + await tx .update(workspaceFiles) .set({ userId: successorId, updatedAt: new Date() }) .where( - inArray( - workspaceFiles.id, - files.map((file) => file.id) + and( + condition, + // A versioned file committed after the history pass keeps its creator until frozen. + or(not(isVersioned), workspaceFileVersionHeadCurrentSql()) ) ) } diff --git a/apps/sim/lib/uploads/contexts/workspace/file-version-store.ts b/apps/sim/lib/uploads/contexts/workspace/file-version-store.ts index a5844e4d9e7..a6c3208b4fd 100644 --- a/apps/sim/lib/uploads/contexts/workspace/file-version-store.ts +++ b/apps/sim/lib/uploads/contexts/workspace/file-version-store.ts @@ -7,7 +7,17 @@ import { workspaceFileVersion, } from '@sim/db/schema' import { generateId } from '@sim/utils/id' -import { and, desc, eq, inArray, isNotNull, lt, notInArray, sql } from 'drizzle-orm' +import { + and, + desc, + eq, + inArray, + isNotNull, + lt, + notInArray, + type SQLWrapper, + sql, +} from 'drizzle-orm' import { type CursorKey, type KeysetKey, @@ -105,14 +115,14 @@ export async function loadWorkspaceFileVersionHead( /** * Batched {@link loadWorkspaceFileVersionHead}: each file's highest version, keyed by file id. - * The max is resolved from the `(file_id, version)` index first, so only head rows are fetched - * rather than every retained version. + * `fileIds` is a subquery, so the file set never becomes a bind-parameter list. The max is + * resolved from the `(file_id, version)` index first, so only head rows are fetched rather than + * every retained version. */ export async function loadWorkspaceFileVersionHeads( - fileIds: string[], + fileIds: SQLWrapper, executor: DbOrTx = db ): Promise> { - if (!fileIds.length) return new Map() const heads = await executor .select(versionSummaryColumns) .from(workspaceFileVersion) @@ -713,27 +723,36 @@ export function getCurrentWorkspaceFileVersion( return withVersionSnapshot(file, currentVersionInSnapshot, executor) } +/** + * Correlated columns for subqueries over the enclosing query's `workspace_files` row. Both sides + * are table-qualified because Drizzle renders single-table columns bare, which would bind the outer + * columns to the subquery's own table. + */ +function correlatedVersionColumns() { + const qualified = (table: typeof workspaceFileVersion | typeof workspaceFiles, name: string) => + sql`${table}.${sql.identifier(name)}` + return { + head: { + fileId: qualified(workspaceFileVersion, workspaceFileVersion.fileId.name), + version: qualified(workspaceFileVersion, workspaceFileVersion.version.name), + key: qualified(workspaceFileVersion, workspaceFileVersion.key.name), + supersededAt: qualified(workspaceFileVersion, workspaceFileVersion.supersededAt.name), + }, + file: { + id: qualified(workspaceFiles, workspaceFiles.id.name), + key: qualified(workspaceFiles, workspaceFiles.key.name), + }, + } +} + /** * The current version number of the enclosing query's `workspace_files` row, as a correlated * subquery so the row and its number come from one statement's snapshot. The newest row numbers * the file's bytes while it still holds them; otherwise the bytes are on the implicit version after * it, and a file with no rows is on version 1 — the numbering {@link implicitCurrentVersion} gives. - * Both sides of the correlation are table-qualified because Drizzle renders single-table columns - * bare, which would bind the outer columns to this subquery's own table. */ export function currentWorkspaceFileVersionNumberSql() { - const qualified = (table: typeof workspaceFileVersion | typeof workspaceFiles, name: string) => - sql`${table}.${sql.identifier(name)}` - const head = { - fileId: qualified(workspaceFileVersion, workspaceFileVersion.fileId.name), - version: qualified(workspaceFileVersion, workspaceFileVersion.version.name), - key: qualified(workspaceFileVersion, workspaceFileVersion.key.name), - supersededAt: qualified(workspaceFileVersion, workspaceFileVersion.supersededAt.name), - } - const file = { - id: qualified(workspaceFiles, workspaceFiles.id.name), - key: qualified(workspaceFiles, workspaceFiles.key.name), - } + const { head, file } = correlatedVersionColumns() const number = sql`case when ${head.supersededAt} is null and ${head.key} = ${file.key} then ${head.version} else ${head.version} + 1 end` return sql`coalesce(( select ${number} from ${workspaceFileVersion} @@ -743,6 +762,17 @@ export function currentWorkspaceFileVersionNumberSql() { ), ${INITIAL_WORKSPACE_FILE_VERSION})`.mapWith(Number) } +/** {@link isVersionHeadCurrent} for the enclosing query's `workspace_files` row. */ +export function workspaceFileVersionHeadCurrentSql() { + const { head, file } = correlatedVersionColumns() + return sql`coalesce(( + select ${head.supersededAt} is null and ${head.key} = ${file.key} from ${workspaceFileVersion} + where ${head.fileId} = ${file.id} + order by ${head.version} desc + limit 1 + ), false)` +} + /** One version of a file, or null when it never existed or retention removed it. */ export function getWorkspaceFileVersion( file: WorkspaceFileVersionSubject, diff --git a/apps/sim/lib/workspace-files/__integration__/file-listing.integration.ts b/apps/sim/lib/workspace-files/__integration__/file-listing.integration.ts index 3c47a1870de..c335d17b854 100644 --- a/apps/sim/lib/workspace-files/__integration__/file-listing.integration.ts +++ b/apps/sim/lib/workspace-files/__integration__/file-listing.integration.ts @@ -679,6 +679,27 @@ describe('bounded workspace file reads in PostgreSQL', () => { ) } }) + + /** + * The file browser's default: every live file, most recently modified first. With sorting + * ruled out, an ordered plan exists only through an index keyed on that order. + */ + it('reads the whole workspace in modified order from its workspace index', async () => { + const nodes = await explainCaptured( + db, + () => + queryWorkspaceFiles(sortWorkspaceId, { + sortBy: 'updatedAt', + sortOrder: 'desc', + limit: 50, + }), + { match: /order by/, gucs: { enable_sort: 'off' } } + ) + expect(nodes.map((node) => node['Node Type'])).not.toContain('Sort') + expect(nodes.map((node) => node['Index Name'])).toContain( + 'workspace_files_workspace_active_updated_idx' + ) + }) }) describe('SQL type labels and kinds', () => { diff --git a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts index 83d23a3dffc..26f117eaaa6 100644 --- a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts +++ b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts @@ -492,6 +492,33 @@ describe('shared resource retention across workspace departure and account erasu ).toMatchObject([{ version: 1, authorUserIds: [fixture.departingId] }]) }) + it('hands off more files than Postgres accepts bind parameters', async () => { + const fixture = await seedResources(false) + /** More ids than Postgres's 65,535 bind parameters per statement. */ + const bulkFileCount = 66_000 + await db.execute(sql` + INSERT INTO ${workspaceFiles} (id, key, user_id, workspace_id, context, original_name, content_type) + SELECT ${fixture.workspaceId} || '-kb-' || n, ${fixture.workspaceId} || '/kb/' || n, + ${fixture.departingId}, ${fixture.workspaceId}, 'knowledge-base', 'bulk.txt', 'text/plain' + FROM generate_series(1, ${bulkFileCount}) AS n + `) + const result = await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect(result.revoked).toBe(true) + expect( + await db + .select({ userId: workspaceFiles.userId, files: sql`count(*)::int` }) + .from(workspaceFiles) + .where(eq(workspaceFiles.workspaceId, fixture.workspaceId)) + .groupBy(workspaceFiles.userId) + ).toEqual([{ userId: fixture.ownerId, files: bulkFileCount + 1 }]) + await assertTransferred(fixture) + }) + it('repairs references left by departures before the handoff fix', async () => { const fixture = await seedResources(true, false) await deleteUserAccount(fixture.departingId, { recordIdentity: false }) diff --git a/apps/sim/tools/airtable/airtable.test.ts b/apps/sim/tools/airtable/airtable.test.ts index 91f547c2da0..247709d0ae6 100644 --- a/apps/sim/tools/airtable/airtable.test.ts +++ b/apps/sim/tools/airtable/airtable.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { airtableCreateRecordsTool } from '@/tools/airtable/create_records' +import { airtableListRecordsTool } from '@/tools/airtable/list_records' import { airtableUpdateMultipleRecordsTool } from '@/tools/airtable/update_multiple_records' import { airtableUpdateRecordTool } from '@/tools/airtable/update_record' import { airtableUpsertRecordsTool } from '@/tools/airtable/upsert_records' @@ -87,3 +88,18 @@ describe.each(requestCases)('Airtable $operation request body', ({ buildBody, bo expect(buildBody(false)).toEqual({ ...body, typecast: false }) }) }) + +describe('airtable_list_records page size', () => { + function listUrl(pageSize: number): URL { + const { url } = airtableListRecordsTool.request + if (typeof url !== 'function') throw new Error('list records url must be a builder') + return new URL(url({ accessToken: 't', baseId: 'app1', tableId: 'tbl1', pageSize })) + } + + it('clamps the page size to the documented 1 to 100 range', () => { + expect(listUrl(500).searchParams.get('pageSize')).toBe('100') + expect(listUrl(2.7).searchParams.get('pageSize')).toBe('2') + expect(listUrl(-3).searchParams.get('pageSize')).toBe('1') + expect(listUrl(0).searchParams.get('pageSize')).toBe('1') + }) +}) diff --git a/apps/sim/tools/airtable/get_base_schema.ts b/apps/sim/tools/airtable/get_base_schema.ts index 5481b89903c..05f358a8846 100644 --- a/apps/sim/tools/airtable/get_base_schema.ts +++ b/apps/sim/tools/airtable/get_base_schema.ts @@ -66,7 +66,7 @@ export const airtableGetBaseSchemaTool: ToolConfig< }, request: { - url: (params) => `https://api.airtable.com/v0/meta/bases/${params.baseId}/tables`, + url: (params) => `https://api.airtable.com/v0/meta/bases/${params.baseId?.trim()}/tables`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/airtable/list_bases.ts b/apps/sim/tools/airtable/list_bases.ts index 6742e652448..977c0643ab4 100644 --- a/apps/sim/tools/airtable/list_bases.ts +++ b/apps/sim/tools/airtable/list_bases.ts @@ -74,7 +74,8 @@ export const airtableListBasesTool: ToolConfig { + it('sends the documented nday value for the saved n-day option', () => { + expect(retentionMode('n-day')).toBe('nday') + }) + + it('passes the other documented modes through', () => { + expect(retentionMode('rolling')).toBe('rolling') + expect(retentionMode('nday_or_before')).toBe('nday_or_before') + }) +}) diff --git a/apps/sim/tools/amplitude/retention.ts b/apps/sim/tools/amplitude/retention.ts index 4ed2b82fff9..7a3fa8f127f 100644 --- a/apps/sim/tools/amplitude/retention.ts +++ b/apps/sim/tools/amplitude/retention.ts @@ -53,13 +53,15 @@ export const retentionTool: ToolConfig = { diff --git a/apps/sim/tools/attio/create_task.ts b/apps/sim/tools/attio/create_task.ts index fb2172b78d1..aa2ac5b1ce1 100644 --- a/apps/sim/tools/attio/create_task.ts +++ b/apps/sim/tools/attio/create_task.ts @@ -75,7 +75,7 @@ export const attioCreateTaskTool: ToolConfig(tool: { request: { body?: (params: P) => unknown } }, params: P): unknown { + return tool.request.body?.(params) +} + +describe('Attio JSON inputs', () => { + it('rejects malformed assignees instead of clearing every assignee on the task', () => { + expect(() => + bodyOf(attioUpdateTaskTool, { accessToken: 't', taskId: 'task-1', assignees: '[{bad' }) + ).toThrow('Invalid JSON provided for assignees') + }) + + it('rejects a malformed filter instead of returning every list entry', () => { + expect(() => + bodyOf(attioQueryListEntriesTool, { accessToken: 't', list: 'list-1', filter: '{bad' }) + ).toThrow('Invalid JSON provided for filter') + }) + + it('rejects malformed subscriptions instead of creating a webhook with none', () => { + expect(() => + bodyOf(attioCreateWebhookTool, { + accessToken: 't', + targetUrl: 'https://example.com/hook', + subscriptions: '[{bad', + }) + ).toThrow('Invalid JSON provided for subscriptions') + }) +}) diff --git a/apps/sim/tools/attio/query_list_entries.ts b/apps/sim/tools/attio/query_list_entries.ts index 6b0574d88b9..1f858980cd7 100644 --- a/apps/sim/tools/attio/query_list_entries.ts +++ b/apps/sim/tools/attio/query_list_entries.ts @@ -73,14 +73,14 @@ export const attioQueryListEntriesTool: ToolConfig< body.filter = typeof params.filter === 'string' ? JSON.parse(params.filter) : params.filter } catch { - body.filter = {} + throw new Error('Invalid JSON provided for filter') } } if (params.sorts) { try { body.sorts = typeof params.sorts === 'string' ? JSON.parse(params.sorts) : params.sorts } catch { - body.sorts = [] + throw new Error('Invalid JSON provided for sorts') } } if (params.limit != null) body.limit = params.limit diff --git a/apps/sim/tools/attio/update_list_entry.ts b/apps/sim/tools/attio/update_list_entry.ts index 760e960cdfc..98aeba33180 100644 --- a/apps/sim/tools/attio/update_list_entry.ts +++ b/apps/sim/tools/attio/update_list_entry.ts @@ -62,7 +62,7 @@ export const attioUpdateListEntryTool: ToolConfig< ? JSON.parse(params.entryValues) : params.entryValues } catch { - entryValues = {} + throw new Error('Invalid JSON provided for entry values') } return { data: { entry_values: entryValues } } }, diff --git a/apps/sim/tools/attio/update_task.ts b/apps/sim/tools/attio/update_task.ts index 66620fa6eb3..f8084b264e0 100644 --- a/apps/sim/tools/attio/update_task.ts +++ b/apps/sim/tools/attio/update_task.ts @@ -73,7 +73,7 @@ export const attioUpdateTaskTool: ToolConfig { + it('omits sort with marker pagination on the root folder', () => { + expect(listQuery({ folderId: '0', usemarker: true, sort: 'name' }).get('sort')).toBeNull() + }) + + it('keeps sort for other folders and for offset pagination on the root', () => { + expect(listQuery({ folderId: '123', usemarker: true, sort: 'name' }).get('sort')).toBe('name') + expect(listQuery({ folderId: '0', sort: 'name' }).get('sort')).toBe('name') + }) +}) + +describe('Box update file description', () => { + function body(params: Record) { + const build = boxUpdateFileTool.request.body + if (!build) throw new Error('update file must build a body') + return build({ accessToken: 't', fileId: '1', ...params } as BoxUpdateFileParams) + } + + it('does not send an untouched null description', () => { + expect(body({ name: 'a.pdf', description: null })).not.toHaveProperty('description') + }) + + it('sends an explicit description, including an empty one', () => { + expect(body({ description: '' })).toHaveProperty('description', '') + }) +}) diff --git a/apps/sim/tools/box/copy_file.ts b/apps/sim/tools/box/copy_file.ts index d83797920a9..63311cedfea 100644 --- a/apps/sim/tools/box/copy_file.ts +++ b/apps/sim/tools/box/copy_file.ts @@ -1,4 +1,5 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxCopyFileParams, BoxUploadFileResponse } from './types' import { UPLOAD_FILE_OUTPUT_PROPERTIES } from './types' @@ -41,7 +42,8 @@ export const boxCopyFileTool: ToolConfig `https://api.box.com/2.0/files/${params.fileId.trim()}/copy`, + url: (params) => + `https://api.box.com/2.0/files/${safeUrlPathSegment(params.fileId, 'fileId')}/copy`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box/delete_file.ts b/apps/sim/tools/box/delete_file.ts index 74d429e4afa..360ae561401 100644 --- a/apps/sim/tools/box/delete_file.ts +++ b/apps/sim/tools/box/delete_file.ts @@ -1,4 +1,5 @@ import type { ToolConfig, ToolResponse } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxDeleteFileParams } from './types' export const boxDeleteFileTool: ToolConfig = { @@ -28,7 +29,7 @@ export const boxDeleteFileTool: ToolConfig = }, request: { - url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`, + url: (params) => `https://api.box.com/2.0/files/${safeUrlPathSegment(params.fileId, 'fileId')}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box/delete_folder.ts b/apps/sim/tools/box/delete_folder.ts index b5fb2206b49..1319bf65e09 100644 --- a/apps/sim/tools/box/delete_folder.ts +++ b/apps/sim/tools/box/delete_folder.ts @@ -1,4 +1,5 @@ import type { ToolConfig, ToolResponse } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxDeleteFolderParams } from './types' export const boxDeleteFolderTool: ToolConfig = { @@ -38,7 +39,7 @@ export const boxDeleteFolderTool: ToolConfig ({ diff --git a/apps/sim/tools/box/download_file.ts b/apps/sim/tools/box/download_file.ts index 20ea1ffdf46..d33c609ad01 100644 --- a/apps/sim/tools/box/download_file.ts +++ b/apps/sim/tools/box/download_file.ts @@ -5,6 +5,7 @@ import type { BoxDownloadFileV2Response, } from '@/tools/box/types' import type { ToolConfig, ToolFileData } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' async function transformDownloadResponse(response: Response) { if (response.status === 202) { @@ -71,7 +72,8 @@ export const boxDownloadFileTool = { }, request: { - url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}/content`, + url: (params) => + `https://api.box.com/2.0/files/${safeUrlPathSegment(params.fileId, 'fileId')}/content`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box/get_file_info.ts b/apps/sim/tools/box/get_file_info.ts index 9fb978a9a00..64f1a648f29 100644 --- a/apps/sim/tools/box/get_file_info.ts +++ b/apps/sim/tools/box/get_file_info.ts @@ -1,6 +1,7 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxFileInfoResponse, BoxGetFileInfoParams } from './types' -import { FILE_OUTPUT_PROPERTIES } from './types' +import { BOX_FILE_FIELDS, FILE_OUTPUT_PROPERTIES } from './types' export const boxGetFileInfoTool: ToolConfig = { id: 'box_get_file_info', @@ -29,7 +30,8 @@ export const boxGetFileInfoTool: ToolConfig `https://api.box.com/2.0/files/${params.fileId.trim()}`, + url: (params) => + `https://api.box.com/2.0/files/${safeUrlPathSegment(params.fileId, 'fileId')}?fields=${BOX_FILE_FIELDS}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box/list_folder_items.ts b/apps/sim/tools/box/list_folder_items.ts index fca1e1e18c0..777a175fe14 100644 --- a/apps/sim/tools/box/list_folder_items.ts +++ b/apps/sim/tools/box/list_folder_items.ts @@ -1,6 +1,7 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxFolderItemsResponse, BoxListFolderItemsParams } from './types' -import { FOLDER_ITEMS_OUTPUT_PROPERTIES } from './types' +import { BOX_FOLDER_ITEM_FIELDS, FOLDER_ITEMS_OUTPUT_PROPERTIES } from './types' export const boxListFolderItemsTool: ToolConfig = { @@ -37,7 +38,20 @@ export const boxListFolderItemsTool: ToolConfig { const queryParams = new URLSearchParams() + queryParams.set('fields', BOX_FOLDER_ITEM_FIELDS) if (params.limit !== undefined) queryParams.set('limit', String(params.limit)) - if (params.offset !== undefined) queryParams.set('offset', String(params.offset)) - if (params.sort) queryParams.set('sort', params.sort) + const markerPaging = Boolean(params.usemarker || params.marker) + if (markerPaging) { + queryParams.set('usemarker', 'true') + if (params.marker) queryParams.set('marker', params.marker) + } else if (params.offset !== undefined) { + queryParams.set('offset', String(params.offset)) + } + /** Box rejects `sort` with marker pagination on the root folder (id `0`). */ + const rootWithMarker = markerPaging && String(params.folderId).trim() === '0' + if (params.sort && !rootWithMarker) queryParams.set('sort', params.sort) if (params.direction) queryParams.set('direction', params.direction) - const qs = queryParams.toString() - return `https://api.box.com/2.0/folders/${params.folderId.trim()}/items${qs ? `?${qs}` : ''}` + return `https://api.box.com/2.0/folders/${safeUrlPathSegment(params.folderId, 'folderId')}/items?${queryParams.toString()}` }, method: 'GET', headers: (params) => ({ @@ -87,9 +109,10 @@ export const boxListFolderItemsTool: ToolConfig> - totalCount: number + totalCount: number | null offset: number limit: number + nextMarker: string | null } } @@ -149,6 +152,33 @@ export interface BoxSearchResponse extends ToolResponse { } } +/** + * `fields` value for file reads and updates. Box returns the standard representation by + * default, which omits `tags` and `comment_count`; naming any field replaces the standard + * set, so every field the file mappers read is listed. + */ +export const BOX_FILE_FIELDS = [ + 'name', + 'description', + 'size', + 'sha1', + 'created_at', + 'modified_at', + 'created_by', + 'modified_by', + 'owned_by', + 'parent', + 'shared_link', + 'tags', + 'comment_count', +].join(',') + +/** + * `fields` value for folder listings. Folder items come back in their mini representation + * (no size or timestamps) unless these are requested; `type` and `id` are always returned. + */ +export const BOX_FOLDER_ITEM_FIELDS = ['name', 'size', 'created_at', 'modified_at'].join(',') + const USER_OUTPUT_PROPERTIES = { id: { type: 'string', description: 'User ID' }, name: { type: 'string', description: 'User name' }, @@ -209,9 +239,20 @@ export const FOLDER_ITEMS_OUTPUT_PROPERTIES = { }, }, }, - totalCount: { type: 'number', description: 'Total number of items in the folder' }, + totalCount: { + type: 'number', + description: + 'Total number of items in the folder; null with marker pagination, where Box omits it', + nullable: true, + }, offset: { type: 'number', description: 'Current pagination offset' }, limit: { type: 'number', description: 'Current pagination limit' }, + nextMarker: { + type: 'string', + description: 'Marker for the next page when using marker-based pagination', + optional: true, + nullable: true, + }, } as const satisfies Record export const FOLDER_OUTPUT_PROPERTIES = { diff --git a/apps/sim/tools/box/update_file.ts b/apps/sim/tools/box/update_file.ts index ad285152fc1..027afb62563 100644 --- a/apps/sim/tools/box/update_file.ts +++ b/apps/sim/tools/box/update_file.ts @@ -1,6 +1,7 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxFileInfoResponse, BoxUpdateFileParams } from './types' -import { FILE_OUTPUT_PROPERTIES } from './types' +import { BOX_FILE_FIELDS, FILE_OUTPUT_PROPERTIES } from './types' export const boxUpdateFileTool: ToolConfig = { id: 'box_update_file', @@ -53,7 +54,8 @@ export const boxUpdateFileTool: ToolConfig `https://api.box.com/2.0/files/${params.fileId.trim()}`, + url: (params) => + `https://api.box.com/2.0/files/${safeUrlPathSegment(params.fileId, 'fileId')}?fields=${BOX_FILE_FIELDS}`, method: 'PUT', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, @@ -62,7 +64,7 @@ export const boxUpdateFileTool: ToolConfig { const body: Record = {} if (params.name) body.name = params.name - if (params.description !== undefined) body.description = params.description + if (typeof params.description === 'string') body.description = params.description if (params.parentFolderId) body.parent = { id: params.parentFolderId.trim() } if (params.tags) body.tags = params.tags diff --git a/apps/sim/tools/box_sign/cancel_request.ts b/apps/sim/tools/box_sign/cancel_request.ts index 8e318fb195f..f77db0dea09 100644 --- a/apps/sim/tools/box_sign/cancel_request.ts +++ b/apps/sim/tools/box_sign/cancel_request.ts @@ -1,4 +1,5 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxSignCancelRequestParams, BoxSignResponse } from './types' import { SIGN_REQUEST_OUTPUT_PROPERTIES } from './types' @@ -29,7 +30,8 @@ export const boxSignCancelRequestTool: ToolConfig `https://api.box.com/2.0/sign_requests/${params.signRequestId}/cancel`, + url: (params) => + `https://api.box.com/2.0/sign_requests/${safeUrlPathSegment(params.signRequestId, 'signRequestId')}/cancel`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box_sign/get_request.ts b/apps/sim/tools/box_sign/get_request.ts index e1819658d1d..93f0c1336e8 100644 --- a/apps/sim/tools/box_sign/get_request.ts +++ b/apps/sim/tools/box_sign/get_request.ts @@ -1,4 +1,5 @@ import type { ToolConfig } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxSignGetRequestParams, BoxSignResponse } from './types' import { SIGN_REQUEST_OUTPUT_PROPERTIES } from './types' @@ -29,7 +30,8 @@ export const boxSignGetRequestTool: ToolConfig `https://api.box.com/2.0/sign_requests/${params.signRequestId}`, + url: (params) => + `https://api.box.com/2.0/sign_requests/${safeUrlPathSegment(params.signRequestId, 'signRequestId')}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box_sign/resend_request.ts b/apps/sim/tools/box_sign/resend_request.ts index 39e9da13fa8..0d709c0414a 100644 --- a/apps/sim/tools/box_sign/resend_request.ts +++ b/apps/sim/tools/box_sign/resend_request.ts @@ -1,4 +1,5 @@ import type { ToolConfig, ToolResponse } from '@/tools/types' +import { safeUrlPathSegment } from '@/tools/url-path' import type { BoxSignResendRequestParams } from './types' export const boxSignResendRequestTool: ToolConfig = { @@ -28,7 +29,8 @@ export const boxSignResendRequestTool: ToolConfig `https://api.box.com/2.0/sign_requests/${params.signRequestId}/resend`, + url: (params) => + `https://api.box.com/2.0/sign_requests/${safeUrlPathSegment(params.signRequestId, 'signRequestId')}/resend`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/box_sign/types.ts b/apps/sim/tools/box_sign/types.ts index e59f2c79e78..0bcb52ee624 100644 --- a/apps/sim/tools/box_sign/types.ts +++ b/apps/sim/tools/box_sign/types.ts @@ -91,7 +91,12 @@ const SIGNER_OUTPUT_PROPERTIES = { const SOURCE_FILE_OUTPUT_PROPERTIES = { id: { type: 'string', description: 'File ID' }, type: { type: 'string', description: 'File type' }, - name: { type: 'string', description: 'File name', optional: true }, + name: { + type: 'string', + description: + 'File name (Box Sign returns source files without a name, so this is usually empty)', + optional: true, + }, } as const satisfies Record export const SIGN_REQUEST_OUTPUT_PROPERTIES = { @@ -99,7 +104,7 @@ export const SIGN_REQUEST_OUTPUT_PROPERTIES = { status: { type: 'string', description: - 'Request status (converting, created, sent, viewed, signed, cancelled, declined, expired, error_converting, error_sending, finalizing, error_finalizing)', + 'Request status (converting, created, sent, viewed, signed, cancelled, declined, error, error_converting, error_sending, expired, finalizing, error_finalizing)', }, name: { type: 'string', description: 'Sign request name', optional: true }, shortId: { type: 'string', description: 'Human-readable short ID', optional: true }, diff --git a/apps/sim/tools/clickup/get_tasks.ts b/apps/sim/tools/clickup/get_tasks.ts index ab63dd1f1cf..fa304f0f32c 100644 --- a/apps/sim/tools/clickup/get_tasks.ts +++ b/apps/sim/tools/clickup/get_tasks.ts @@ -12,7 +12,7 @@ export const clickupGetTasksTool: ToolConfig mapClickUpTask(task)) }, + output: { + tasks: rawTasks.map((task: unknown) => mapClickUpTask(task)), + lastPage: typeof data?.last_page === 'boolean' ? data.last_page : null, + }, } }, @@ -186,5 +225,11 @@ export const clickupGetTasksTool: ToolConfig +/** Pagination outputs shared by the envelope and template list tools */ +export const LIST_PAGINATION_OUTPUTS = { + startPosition: { + type: 'number', + description: 'Zero-based index of the first result in this page', + optional: true, + nullable: true, + }, + endPosition: { + type: 'number', + description: 'Zero-based index of the last result in this page', + optional: true, + nullable: true, + }, + nextUri: { + type: 'string', + description: 'URI of the next page of results; null on the last page', + optional: true, + nullable: true, + }, +} as const satisfies Record + export const ENVELOPES_ARRAY_OUTPUT: OutputProperty = { type: 'array', description: 'Array of DocuSign envelopes', @@ -117,6 +139,7 @@ export interface DocuSignListEnvelopesParams { envelopeStatus?: string searchText?: string count?: string + startPosition?: string } export interface DocuSignVoidEnvelopeParams { @@ -138,6 +161,7 @@ export interface DocuSignListTemplatesParams { instanceUrl?: string searchText?: string count?: string + startPosition?: string } export interface DocuSignListRecipientsParams { @@ -193,6 +217,9 @@ export interface DocuSignListEnvelopesResponse extends ToolResponse { }> totalSetSize: number resultSetSize: number + startPosition: number | null + endPosition: number | null + nextUri: string | null } } @@ -224,6 +251,9 @@ export interface DocuSignListTemplatesResponse extends ToolResponse { }> totalSetSize: number resultSetSize: number + startPosition: number | null + endPosition: number | null + nextUri: string | null } } diff --git a/apps/sim/tools/docusign/utils.ts b/apps/sim/tools/docusign/utils.ts new file mode 100644 index 00000000000..54e9a0ab280 --- /dev/null +++ b/apps/sim/tools/docusign/utils.ts @@ -0,0 +1,19 @@ +import { toStringOrNull } from '@sim/utils/coerce' + +function position(value: unknown): number | null { + if (value === undefined || value === null || value === '') return null + const parsed = Number(value) + return Number.isFinite(parsed) ? parsed : null +} + +/** + * Reads the pagination fields DocuSign returns as strings on list responses + * (`startPosition`, `endPosition`, `nextUri`). + */ +export function listPagination(data: Record) { + return { + startPosition: position(data.startPosition), + endPosition: position(data.endPosition), + nextUri: toStringOrNull(data.nextUri) || null, + } +} diff --git a/apps/sim/tools/dropbox/create_shared_link.ts b/apps/sim/tools/dropbox/create_shared_link.ts index b7b05be3845..f8256c24c15 100644 --- a/apps/sim/tools/dropbox/create_shared_link.ts +++ b/apps/sim/tools/dropbox/create_shared_link.ts @@ -90,10 +90,10 @@ export const dropboxCreateSharedLinkTool: ToolConfig< if (!response.ok) { // A link may already exist for this path - Dropbox includes its metadata in the error // when the requested settings match the existing link, so surface it as a success. - // Per Stone's JSON rules, a union member whose payload is a struct (SharedLinkMetadata) - // flattens that struct's fields alongside ".tag" - there is no nested "metadata" key. - const existingLink = data.error?.shared_link_already_exists - if (existingLink) { + // SharedLinkMetadata has enumerated subtypes (file/folder), so Stone nests it under the + // union tag: error.shared_link_already_exists = { ".tag": "metadata", metadata: {...} }. + const existingLink = data.error?.shared_link_already_exists?.metadata + if (existingLink?.url) { return { success: true, output: { diff --git a/apps/sim/tools/dropbox/dropbox.test.ts b/apps/sim/tools/dropbox/dropbox.test.ts new file mode 100644 index 00000000000..4bbc51c1a8b --- /dev/null +++ b/apps/sim/tools/dropbox/dropbox.test.ts @@ -0,0 +1,67 @@ +/** + * @vitest-environment node + */ +import { describe, expect, it } from 'vitest' +import { dropboxCreateSharedLinkTool } from '@/tools/dropbox/create_shared_link' +import { dropboxListFolderTool } from '@/tools/dropbox/list_folder' +import { dropboxSearchTool } from '@/tools/dropbox/search' +import type { ToolConfig } from '@/tools/types' + +function resolveRequest

(tool: ToolConfig

, params: P) { + const { url, body } = tool.request + return { + url: typeof url === 'function' ? url(params) : url, + body: body?.(params), + } +} + +describe('dropbox_create_shared_link', () => { + it('returns the existing link that Dropbox nests under the shared_link_already_exists union', async () => { + const existing = { + '.tag': 'file', + url: 'https://www.dropbox.com/s/abc/report.pdf?dl=0', + name: 'report.pdf', + path_lower: '/report.pdf', + link_permissions: { can_revoke: true, resolved_visibility: { '.tag': 'public' } }, + } + const response = Response.json( + { + error_summary: 'shared_link_already_exists/metadata/..', + error: { + '.tag': 'shared_link_already_exists', + shared_link_already_exists: { '.tag': 'metadata', metadata: existing }, + }, + }, + { status: 409 } + ) + + const result = await dropboxCreateSharedLinkTool.transformResponse?.(response) + + expect(result?.success).toBe(true) + expect(result?.output.sharedLink?.url).toBe(existing.url) + }) +}) + +describe('cursor pagination', () => { + it('continues a folder listing from a cursor', () => { + expect( + resolveRequest(dropboxListFolderTool, { path: '/docs', recursive: true, cursor: ' abc ' }) + ).toEqual({ + url: 'https://api.dropboxapi.com/2/files/list_folder/continue', + body: { cursor: 'abc' }, + }) + expect(resolveRequest(dropboxListFolderTool, { path: '/docs' }).url).toBe( + 'https://api.dropboxapi.com/2/files/list_folder' + ) + }) + + it('continues a search from a cursor', () => { + expect(resolveRequest(dropboxSearchTool, { query: 'invoice', cursor: 'xyz' })).toEqual({ + url: 'https://api.dropboxapi.com/2/files/search/continue_v2', + body: { cursor: 'xyz' }, + }) + expect(resolveRequest(dropboxSearchTool, { query: 'invoice' }).url).toBe( + 'https://api.dropboxapi.com/2/files/search_v2' + ) + }) +}) diff --git a/apps/sim/tools/dropbox/list_folder.ts b/apps/sim/tools/dropbox/list_folder.ts index a0a31ba9f1b..280d44c0aac 100644 --- a/apps/sim/tools/dropbox/list_folder.ts +++ b/apps/sim/tools/dropbox/list_folder.ts @@ -16,9 +16,10 @@ export const dropboxListFolderTool: ToolConfig + params.cursor?.trim() + ? 'https://api.dropboxapi.com/2/files/list_folder/continue' + : 'https://api.dropboxapi.com/2/files/list_folder', method: 'POST', headers: (params) => { if (!params.accessToken) { @@ -58,13 +69,21 @@ export const dropboxListFolderTool: ToolConfig ({ - path: params.path.trim() === '/' ? '' : params.path.trim(), - recursive: params.recursive ?? false, - include_deleted: params.includeDeleted ?? false, - include_media_info: params.includeMediaInfo ?? false, - limit: params.limit, - }), + body: (params) => { + const cursor = params.cursor?.trim() + if (cursor) return { cursor } + if (typeof params.path !== 'string') { + throw new Error('path is required unless cursor is set (use "" for the root folder)') + } + const path = params.path.trim() + return { + path: path === '/' ? '' : path, + recursive: params.recursive ?? false, + include_deleted: params.includeDeleted ?? false, + include_media_info: params.includeMediaInfo ?? false, + limit: params.limit, + } + }, }, transformResponse: async (response) => { @@ -105,7 +124,7 @@ export const dropboxListFolderTool: ToolConfig + params.cursor?.trim() + ? 'https://api.dropboxapi.com/2/files/search/continue_v2' + : 'https://api.dropboxapi.com/2/files/search_v2', method: 'POST', headers: (params) => { if (!params.accessToken) { @@ -52,6 +62,12 @@ export const dropboxSearchTool: ToolConfig { + const cursor = params.cursor?.trim() + if (cursor) return { cursor } + if (!params.query?.trim()) { + throw new Error('query is required unless cursor is set') + } + const body: Record = { query: params.query, } @@ -130,7 +146,8 @@ export const dropboxSearchTool: ToolConfig extends ToolResponse } export interface DropboxListFolderParams extends DropboxBaseParams { - path: string + path?: string recursive?: boolean includeDeleted?: boolean includeMediaInfo?: boolean limit?: number + cursor?: string } export interface DropboxListFolderResponse extends ToolResponse { @@ -188,10 +189,11 @@ export interface DropboxCreateSharedLinkResponse extends ToolResponse { } export interface DropboxSearchParams extends DropboxBaseParams { - query: string + query?: string path?: string fileExtensions?: string maxResults?: number + cursor?: string } export interface DropboxSearchResponse extends ToolResponse { diff --git a/apps/sim/tools/generated/tool-ids.ts b/apps/sim/tools/generated/tool-ids.ts index 1aec7380f7f..38963fc6793 100644 --- a/apps/sim/tools/generated/tool-ids.ts +++ b/apps/sim/tools/generated/tool-ids.ts @@ -3,7 +3,7 @@ /** Every registered tool id, including versioned variants. */ const toolIds: string[] = JSON.parse( - '["a2a_cancel_task","a2a_get_agent_card","a2a_get_task","a2a_send_message","affinity_batch_update_entity_fields","affinity_batch_update_list_entry_fields","affinity_create_list","affinity_create_list_field_dropdown_option","affinity_create_merge","affinity_create_note","affinity_create_reminder","affinity_delete_list_field_dropdown_option","affinity_delete_note","affinity_get_company","affinity_get_current_user","affinity_get_entity_field_value","affinity_get_list","affinity_get_list_entry","affinity_get_list_entry_field","affinity_get_list_field_dropdown_option","affinity_get_merge","affinity_get_merge_task","affinity_get_note","affinity_get_opportunity","affinity_get_person","affinity_get_saved_view","affinity_get_transcript","affinity_get_user","affinity_list_calls","affinity_list_chat_messages","affinity_list_companies","affinity_list_coworker_connections","affinity_list_emails","affinity_list_entity_field_values","affinity_list_entity_list_entries","affinity_list_entity_lists","affinity_list_entity_notes","affinity_list_entity_relationships","affinity_list_field_dropdown_options","affinity_list_field_metadata","affinity_list_field_value_changes","affinity_list_investor_executive_connections","affinity_list_list_entries","affinity_list_list_entry_field_value_changes","affinity_list_list_entry_fields","affinity_list_list_field_dropdown_options","affinity_list_list_fields","affinity_list_lists","affinity_list_meetings","affinity_list_merge_tasks","affinity_list_merges","affinity_list_note_attached_companies","affinity_list_note_attached_opportunities","affinity_list_note_attached_persons","affinity_list_note_replies","affinity_list_notes","affinity_list_opportunities","affinity_list_persons","affinity_list_reminders","affinity_list_saved_view_entries","affinity_list_saved_views","affinity_list_transcript_fragments","affinity_list_transcripts","affinity_list_users","affinity_search_companies","affinity_search_files","affinity_search_list_entries","affinity_search_notes","affinity_search_persons","affinity_semantic_search","affinity_update_entity_field_value","affinity_update_list_entry_field","affinity_update_list_field_dropdown_option","affinity_update_note","agentmail_create_draft","agentmail_create_inbox","agentmail_delete_draft","agentmail_delete_inbox","agentmail_delete_thread","agentmail_forward_message","agentmail_get_draft","agentmail_get_inbox","agentmail_get_message","agentmail_get_thread","agentmail_list_drafts","agentmail_list_inboxes","agentmail_list_messages","agentmail_list_threads","agentmail_reply_message","agentmail_send_draft","agentmail_send_message","agentmail_update_draft","agentmail_update_inbox","agentmail_update_message","agentmail_update_thread","agentphone_create_call","agentphone_create_contact","agentphone_create_number","agentphone_delete_contact","agentphone_get_call","agentphone_get_call_transcript","agentphone_get_contact","agentphone_get_conversation","agentphone_get_conversation_messages","agentphone_get_number_messages","agentphone_get_usage","agentphone_get_usage_daily","agentphone_get_usage_monthly","agentphone_list_calls","agentphone_list_contacts","agentphone_list_conversations","agentphone_list_numbers","agentphone_react_to_message","agentphone_release_number","agentphone_send_message","agentphone_update_contact","agentphone_update_conversation","agiloft_async_status","agiloft_attach_file","agiloft_attachment_info","agiloft_create_record","agiloft_delete_record","agiloft_get_choice_line_id","agiloft_list_tables","agiloft_lock_record","agiloft_nlp_search","agiloft_read_record","agiloft_remove_attachment","agiloft_retrieve_attachment","agiloft_run_action_button","agiloft_saved_search","agiloft_search_records","agiloft_select_records","agiloft_update_record","agiloft_upsert_record","ahrefs_anchors","ahrefs_backlinks","ahrefs_backlinks_stats","ahrefs_batch_analysis","ahrefs_broken_backlinks","ahrefs_domain_rating","ahrefs_domain_rating_history","ahrefs_keyword_overview","ahrefs_keywords_history","ahrefs_metrics","ahrefs_metrics_history","ahrefs_organic_competitors","ahrefs_organic_keywords","ahrefs_paid_pages","ahrefs_rank_tracker_competitors_overview","ahrefs_rank_tracker_competitors_stats","ahrefs_rank_tracker_overview","ahrefs_rank_tracker_serp_overview","ahrefs_refdomains_history","ahrefs_referring_domains","ahrefs_related_terms","ahrefs_site_audit_page_explorer","ahrefs_top_pages","airtable_create_records","airtable_delete_records","airtable_get_base_schema","airtable_get_record","airtable_list_bases","airtable_list_records","airtable_list_tables","airtable_update_multiple_records","airtable_update_record","airtable_upsert_records","algolia_add_record","algolia_batch_operations","algolia_browse_records","algolia_clear_records","algolia_copy_move_index","algolia_delete_by_filter","algolia_delete_index","algolia_delete_record","algolia_get_record","algolia_get_records","algolia_get_settings","algolia_get_task_status","algolia_list_indices","algolia_partial_update_record","algolia_search","algolia_update_settings","amplitude_event_segmentation","amplitude_funnels","amplitude_get_active_users","amplitude_get_revenue","amplitude_group_identify","amplitude_identify_user","amplitude_list_events","amplitude_realtime_active_users","amplitude_retention","amplitude_send_event","amplitude_user_activity","amplitude_user_profile","amplitude_user_search","apify_get_dataset_items","apify_get_run","apify_run_actor_async","apify_run_actor_sync","apify_run_task","apollo_account_bulk_create","apollo_account_bulk_update","apollo_account_create","apollo_account_search","apollo_account_update","apollo_contact_bulk_create","apollo_contact_bulk_update","apollo_contact_create","apollo_contact_search","apollo_contact_update","apollo_email_accounts","apollo_opportunity_create","apollo_opportunity_get","apollo_opportunity_search","apollo_opportunity_update","apollo_organization_bulk_enrich","apollo_organization_enrich","apollo_organization_search","apollo_people_bulk_enrich","apollo_people_enrich","apollo_people_search","apollo_sequence_add_contacts","apollo_sequence_search","apollo_task_create","apollo_task_search","appconfig_create_application","appconfig_create_configuration_profile","appconfig_create_environment","appconfig_create_hosted_configuration_version","appconfig_delete_application","appconfig_delete_configuration_profile","appconfig_delete_environment","appconfig_delete_hosted_configuration_version","appconfig_get_application","appconfig_get_configuration","appconfig_get_configuration_profile","appconfig_get_deployment","appconfig_get_environment","appconfig_get_hosted_configuration_version","appconfig_list_applications","appconfig_list_configuration_profiles","appconfig_list_deployment_strategies","appconfig_list_deployments","appconfig_list_environments","appconfig_list_hosted_configuration_versions","appconfig_start_deployment","appconfig_stop_deployment","appconfig_update_application","appconfig_update_configuration_profile","appconfig_update_environment","arxiv_get_author_papers","arxiv_get_paper","arxiv_search","asana_add_comment","asana_add_followers","asana_create_project","asana_create_section","asana_create_subtask","asana_create_task","asana_delete_task","asana_get_project","asana_get_projects","asana_get_task","asana_list_sections","asana_list_workspaces","asana_search_tasks","asana_update_task","ashby_add_candidate_tag","ashby_anonymize_candidate","ashby_change_application_source","ashby_change_application_stage","ashby_create_application","ashby_create_candidate","ashby_create_note","ashby_delete_application","ashby_get_application","ashby_get_candidate","ashby_get_job","ashby_get_job_posting","ashby_get_offer","ashby_get_opening","ashby_list_application_feedback","ashby_list_application_history","ashby_list_applications","ashby_list_archive_reasons","ashby_list_candidate_tags","ashby_list_candidates","ashby_list_custom_fields","ashby_list_departments","ashby_list_interview_plans","ashby_list_interview_stages","ashby_list_interviews","ashby_list_job_postings","ashby_list_jobs","ashby_list_locations","ashby_list_notes","ashby_list_offers","ashby_list_openings","ashby_list_sources","ashby_list_users","ashby_remove_candidate_tag","ashby_search_candidates","ashby_search_jobs","ashby_search_openings","ashby_search_users","ashby_set_custom_field_value","ashby_set_custom_field_values","ashby_transfer_application","ashby_update_candidate","ashby_upload_candidate_file","ashby_upload_resume","athena_batch_get_named_query","athena_batch_get_prepared_statement","athena_batch_get_query_execution","athena_create_named_query","athena_create_prepared_statement","athena_delete_named_query","athena_delete_prepared_statement","athena_get_data_catalog","athena_get_database","athena_get_named_query","athena_get_prepared_statement","athena_get_query_execution","athena_get_query_results","athena_get_query_runtime_statistics","athena_get_table_metadata","athena_get_work_group","athena_list_data_catalogs","athena_list_databases","athena_list_named_queries","athena_list_prepared_statements","athena_list_query_executions","athena_list_table_metadata","athena_list_work_groups","athena_start_query","athena_stop_query","athena_update_named_query","athena_update_prepared_statement","attio_assert_record","attio_create_attribute","attio_create_comment","attio_create_list","attio_create_list_entry","attio_create_note","attio_create_object","attio_create_record","attio_create_task","attio_create_webhook","attio_delete_comment","attio_delete_list_entry","attio_delete_note","attio_delete_record","attio_delete_task","attio_delete_webhook","attio_get_attribute","attio_get_comment","attio_get_list","attio_get_list_entry","attio_get_member","attio_get_note","attio_get_object","attio_get_record","attio_get_task","attio_get_thread","attio_get_webhook","attio_list_attributes","attio_list_lists","attio_list_members","attio_list_notes","attio_list_objects","attio_list_records","attio_list_tasks","attio_list_threads","attio_list_webhooks","attio_query_list_entries","attio_search_records","attio_update_attribute","attio_update_list","attio_update_list_entry","attio_update_object","attio_update_record","attio_update_task","attio_update_webhook","azure_data_explorer_create_table","azure_data_explorer_drop_table","azure_data_explorer_ingest_from_query","azure_data_explorer_ingest_inline","azure_data_explorer_list_databases","azure_data_explorer_list_functions","azure_data_explorer_list_tables","azure_data_explorer_management","azure_data_explorer_query","azure_data_explorer_show_database_schema","azure_data_explorer_show_ingestion_failures","azure_data_explorer_show_operations","azure_data_explorer_show_table_details","azure_data_explorer_show_table_schema","azure_devops_add_comment","azure_devops_create_work_item","azure_devops_get_build_log","azure_devops_get_build_timeline","azure_devops_get_comments","azure_devops_get_pipeline","azure_devops_get_pipeline_run","azure_devops_get_work_item","azure_devops_get_work_items_batch","azure_devops_get_work_items_between_builds","azure_devops_list_build_logs","azure_devops_list_builds","azure_devops_list_pipeline_runs","azure_devops_list_pipelines","azure_devops_query_work_items","azure_devops_update_work_item","bitbucket_approve_pull_request","bitbucket_create_branch","bitbucket_create_pull_request","bitbucket_create_pull_request_comment","bitbucket_decline_pull_request","bitbucket_delete_branch","bitbucket_get_commit","bitbucket_get_file","bitbucket_get_file_metadata","bitbucket_get_pipeline","bitbucket_get_pipeline_step_log","bitbucket_get_pull_request","bitbucket_get_pull_request_diff","bitbucket_get_pull_request_diffstat","bitbucket_get_pull_request_merge_task_status","bitbucket_get_repository","bitbucket_list_branches","bitbucket_list_commits","bitbucket_list_directory","bitbucket_list_pipeline_steps","bitbucket_list_pipelines","bitbucket_list_pull_request_comments","bitbucket_list_pull_request_commit_statuses","bitbucket_list_pull_requests","bitbucket_list_repositories","bitbucket_list_workspaces","bitbucket_merge_pull_request","bitbucket_request_pull_request_changes","bitbucket_stop_pipeline","bitbucket_trigger_pipeline","box_copy_file","box_create_folder","box_delete_file","box_delete_folder","box_download_file","box_download_file_v2","box_get_file_info","box_list_folder_items","box_search","box_sign_cancel_request","box_sign_create_request","box_sign_get_request","box_sign_list_requests","box_sign_resend_request","box_update_file","box_upload_file","brandfetch_get_brand","brandfetch_search","brex_archive_budget","brex_create_budget","brex_create_spend_limit","brex_create_transfer","brex_create_vendor","brex_get_budget","brex_get_cash_account","brex_get_company","brex_get_current_user","brex_get_expense","brex_get_spend_limit","brex_get_transfer","brex_get_user","brex_get_vendor","brex_list_budgets","brex_list_card_accounts","brex_list_card_statements","brex_list_card_transactions","brex_list_cards","brex_list_cash_accounts","brex_list_cash_statements","brex_list_cash_transactions","brex_list_departments","brex_list_expenses","brex_list_locations","brex_list_spend_limits","brex_list_titles","brex_list_transfers","brex_list_users","brex_list_vendors","brex_match_receipt","brex_update_expense","brex_update_vendor","brex_upload_receipt","brightdata_cancel_snapshot","brightdata_discover","brightdata_download_snapshot","brightdata_scrape_dataset","brightdata_scrape_url","brightdata_serp_search","brightdata_snapshot_status","brightdata_sync_scrape","browser_use_run_task","buffer_create_idea","buffer_create_post","buffer_delete_post","buffer_edit_post","buffer_get_account","buffer_get_aggregated_post_metrics","buffer_get_channel","buffer_get_channels","buffer_get_daily_posting_limits","buffer_get_idea_groups","buffer_get_ideas","buffer_get_post","buffer_get_posts","calcom_cancel_booking","calcom_confirm_booking","calcom_create_booking","calcom_create_event_type","calcom_create_schedule","calcom_decline_booking","calcom_delete_event_type","calcom_delete_schedule","calcom_get_booking","calcom_get_default_schedule","calcom_get_event_type","calcom_get_schedule","calcom_get_slots","calcom_list_bookings","calcom_list_event_types","calcom_list_schedules","calcom_reschedule_booking","calcom_update_event_type","calcom_update_schedule","calendly_cancel_event","calendly_create_event_invitee","calendly_create_invitee_no_show","calendly_create_scheduling_link","calendly_create_webhook","calendly_delete_invitee_no_show","calendly_delete_webhook","calendly_get_current_user","calendly_get_event_invitee","calendly_get_event_type","calendly_get_scheduled_event","calendly_get_user","calendly_list_event_invitees","calendly_list_event_type_available_times","calendly_list_event_types","calendly_list_organization_memberships","calendly_list_routing_form_submissions","calendly_list_routing_forms","calendly_list_scheduled_events","calendly_list_user_availability_schedules","calendly_list_user_busy_times","calendly_list_webhooks","cbinsights_chat","cbinsights_get_commercial_maturity_history","cbinsights_get_exit_probability_history","cbinsights_get_mosaic_history","cbinsights_get_org_business_relationships","cbinsights_get_org_funding_window","cbinsights_get_org_fundings","cbinsights_get_org_investments","cbinsights_get_org_management_and_board","cbinsights_get_org_outlook","cbinsights_get_org_portfolio_exits","cbinsights_get_org_revenue","cbinsights_get_scouting_report","cbinsights_get_strategy_map","cbinsights_list_business_relationships","cbinsights_list_funding_window","cbinsights_list_fundings","cbinsights_list_investments","cbinsights_list_management_and_board","cbinsights_list_outlook","cbinsights_list_portfolio_exits","cbinsights_list_revenue","cbinsights_lookup_organizations","cbinsights_rag","cbinsights_search_firmographics","checkr_add_report_tag","checkr_apply_report_review_action","checkr_cancel_adverse_action","checkr_cancel_continuous_check","checkr_cancel_invitation","checkr_cancel_subscription","checkr_complete_report","checkr_create_adverse_action","checkr_create_candidate","checkr_create_continuous_check","checkr_create_geo","checkr_create_invitation","checkr_create_report","checkr_create_subscription","checkr_delete_candidate_pii","checkr_delete_geo","checkr_get_account","checkr_get_adverse_action","checkr_get_candidate","checkr_get_continuous_check","checkr_get_document","checkr_get_geo","checkr_get_invitation","checkr_get_node","checkr_get_package","checkr_get_program","checkr_get_report","checkr_get_report_eta","checkr_get_report_progressive_status","checkr_get_report_tags","checkr_get_screening","checkr_get_subscription","checkr_get_verification","checkr_list_adverse_actions","checkr_list_adverse_items","checkr_list_assessments","checkr_list_candidate_documents","checkr_list_candidates","checkr_list_continuous_checks","checkr_list_counties","checkr_list_geos","checkr_list_invitations","checkr_list_nodes","checkr_list_packages","checkr_list_programs","checkr_list_report_addresses","checkr_list_subscriptions","checkr_list_users","checkr_list_verifications","checkr_remove_report_tag","checkr_set_report_tags","checkr_update_candidate","checkr_update_continuous_check","checkr_update_geo","checkr_update_report","checkr_update_subscription","circleback_add_tag_to_meetings","circleback_create_tag","circleback_delete_action_item","circleback_delete_meeting","circleback_delete_tag","circleback_get_company","circleback_get_meeting","circleback_get_person","circleback_get_transcript","circleback_list_action_items","circleback_list_calendar_events","circleback_list_companies","circleback_list_meetings","circleback_list_people","circleback_list_tags","circleback_remove_tag_from_meetings","circleback_search_meetings","circleback_update_action_item","circleback_update_meeting","circleback_update_tag","clay_populate","clerk_add_organization_member","clerk_ban_user","clerk_create_actor_token","clerk_create_allowlist_identifier","clerk_create_blocklist_identifier","clerk_create_organization","clerk_create_organization_invitation","clerk_create_user","clerk_delete_allowlist_identifier","clerk_delete_blocklist_identifier","clerk_delete_organization","clerk_delete_user","clerk_get_jwt_template","clerk_get_organization","clerk_get_session","clerk_get_user","clerk_get_user_oauth_token","clerk_list_allowlist_identifiers","clerk_list_blocklist_identifiers","clerk_list_jwt_templates","clerk_list_organization_invitations","clerk_list_organization_memberships","clerk_list_organizations","clerk_list_sessions","clerk_list_users","clerk_lock_user","clerk_remove_organization_member","clerk_revoke_actor_token","clerk_revoke_session","clerk_unban_user","clerk_unlock_user","clerk_update_organization","clerk_update_organization_membership","clerk_update_user","clickhouse_count_rows","clickhouse_create_database","clickhouse_create_table","clickhouse_delete","clickhouse_describe_table","clickhouse_drop_database","clickhouse_drop_partition","clickhouse_drop_table","clickhouse_execute","clickhouse_insert","clickhouse_insert_rows","clickhouse_introspect","clickhouse_kill_query","clickhouse_list_clusters","clickhouse_list_databases","clickhouse_list_mutations","clickhouse_list_partitions","clickhouse_list_running_queries","clickhouse_list_tables","clickhouse_optimize_table","clickhouse_query","clickhouse_rename_table","clickhouse_show_create_table","clickhouse_table_stats","clickhouse_truncate_table","clickhouse_update","clickup_add_tag_to_task","clickup_create_checklist","clickup_create_checklist_item","clickup_create_comment","clickup_create_folder","clickup_create_list","clickup_create_task","clickup_create_time_entry","clickup_delete_checklist","clickup_delete_checklist_item","clickup_delete_comment","clickup_delete_task","clickup_delete_time_entry","clickup_get_comments","clickup_get_custom_fields","clickup_get_folders","clickup_get_list_members","clickup_get_lists","clickup_get_running_timer","clickup_get_space_tags","clickup_get_spaces","clickup_get_task","clickup_get_task_members","clickup_get_tasks","clickup_get_time_entries","clickup_get_workspaces","clickup_remove_custom_field_value","clickup_remove_tag_from_task","clickup_search_tasks","clickup_set_custom_field_value","clickup_start_timer","clickup_stop_timer","clickup_update_checklist","clickup_update_checklist_item","clickup_update_comment","clickup_update_task","clickup_update_time_entry","clickup_upload_attachment","cloudflare_create_access_application","cloudflare_create_access_group","cloudflare_create_access_policy","cloudflare_create_access_service_token","cloudflare_create_dns_record","cloudflare_create_r2_bucket","cloudflare_create_rate_limit_rule","cloudflare_create_ruleset","cloudflare_create_ruleset_rule","cloudflare_create_tunnel","cloudflare_create_worker_route","cloudflare_create_zone","cloudflare_delete_access_application","cloudflare_delete_access_group","cloudflare_delete_access_policy","cloudflare_delete_dns_record","cloudflare_delete_r2_bucket","cloudflare_delete_r2_bucket_cors","cloudflare_delete_ruleset","cloudflare_delete_ruleset_rule","cloudflare_delete_tunnel","cloudflare_delete_worker_route","cloudflare_delete_worker_script","cloudflare_delete_zone","cloudflare_dns_analytics","cloudflare_get_access_application","cloudflare_get_access_group","cloudflare_get_access_policy","cloudflare_get_access_service_token","cloudflare_get_dns_record","cloudflare_get_r2_bucket","cloudflare_get_r2_bucket_cors","cloudflare_get_r2_bucket_lifecycle","cloudflare_get_ruleset","cloudflare_get_ruleset_entrypoint","cloudflare_get_tunnel","cloudflare_get_tunnel_configuration","cloudflare_get_tunnel_token","cloudflare_get_worker_route","cloudflare_get_worker_script_settings","cloudflare_get_zone","cloudflare_get_zone_settings","cloudflare_list_access_applications","cloudflare_list_access_groups","cloudflare_list_access_identity_providers","cloudflare_list_access_policies","cloudflare_list_access_service_tokens","cloudflare_list_certificates","cloudflare_list_dns_records","cloudflare_list_managed_ruleset_overrides","cloudflare_list_r2_buckets","cloudflare_list_rate_limit_rules","cloudflare_list_rulesets","cloudflare_list_tunnels","cloudflare_list_worker_routes","cloudflare_list_worker_scripts","cloudflare_list_zones","cloudflare_purge_cache","cloudflare_refresh_access_service_token","cloudflare_revoke_access_service_token","cloudflare_rotate_access_service_token","cloudflare_update_access_application","cloudflare_update_access_group","cloudflare_update_access_policy","cloudflare_update_access_service_token","cloudflare_update_dns_record","cloudflare_update_r2_bucket_cors","cloudflare_update_r2_bucket_lifecycle","cloudflare_update_rate_limit_rule","cloudflare_update_ruleset","cloudflare_update_ruleset_rule","cloudflare_update_tunnel","cloudflare_update_tunnel_configuration","cloudflare_update_worker_route","cloudflare_update_zone","cloudflare_update_zone_setting","cloudformation_cancel_update_stack","cloudformation_create_change_set","cloudformation_create_stack","cloudformation_delete_stack","cloudformation_describe_change_set","cloudformation_describe_stack_drift_detection_status","cloudformation_describe_stack_events","cloudformation_describe_stacks","cloudformation_detect_stack_drift","cloudformation_execute_change_set","cloudformation_get_template","cloudformation_get_template_summary","cloudformation_list_stack_resources","cloudformation_update_stack","cloudformation_validate_template","cloudtrail_cancel_query","cloudtrail_describe_query","cloudtrail_describe_trails","cloudtrail_get_event_data_store","cloudtrail_get_event_selectors","cloudtrail_get_insight_selectors","cloudtrail_get_query_results","cloudtrail_get_trail","cloudtrail_get_trail_status","cloudtrail_list_event_data_stores","cloudtrail_list_tags","cloudtrail_list_trails","cloudtrail_lookup_events","cloudtrail_start_query","cloudwatch_describe_alarm_history","cloudwatch_describe_alarms","cloudwatch_describe_log_groups","cloudwatch_describe_log_streams","cloudwatch_filter_log_events","cloudwatch_get_log_events","cloudwatch_get_metric_statistics","cloudwatch_list_metrics","cloudwatch_mute_alarm","cloudwatch_put_log_group_retention","cloudwatch_put_metric_data","cloudwatch_query_logs","cloudwatch_unmute_alarm","coda_add_custom_domain","coda_add_permission","coda_change_user_role","coda_create_doc","coda_create_folder","coda_create_page","coda_delete_custom_domain","coda_delete_doc","coda_delete_folder","coda_delete_page","coda_delete_page_content","coda_delete_permission","coda_delete_row","coda_delete_rows","coda_export_page","coda_get_acl_settings","coda_get_analytics_last_updated","coda_get_column","coda_get_control","coda_get_custom_domain_provider","coda_get_doc","coda_get_doc_analytics_summary","coda_get_folder","coda_get_formula","coda_get_mutation_status","coda_get_page","coda_get_page_content","coda_get_page_export_status","coda_get_row","coda_get_sharing_metadata","coda_get_table","coda_list_categories","coda_list_columns","coda_list_controls","coda_list_custom_domains","coda_list_doc_analytics","coda_list_docs","coda_list_folder_children","coda_list_folders","coda_list_formulas","coda_list_page_analytics","coda_list_pages","coda_list_permissions","coda_list_rows","coda_list_tables","coda_list_workspace_members","coda_list_workspace_roles","coda_publish_doc","coda_push_button","coda_resolve_browser_link","coda_search_principals","coda_trigger_automation","coda_unpublish_doc","coda_update_acl_settings","coda_update_doc","coda_update_folder","coda_update_page","coda_update_row","coda_upsert_rows","coda_whoami","codepipeline_disable_stage_transition","codepipeline_enable_stage_transition","codepipeline_get_pipeline","codepipeline_get_pipeline_execution","codepipeline_get_pipeline_state","codepipeline_list_action_executions","codepipeline_list_pipeline_executions","codepipeline_list_pipelines","codepipeline_put_approval_result","codepipeline_retry_stage_execution","codepipeline_start_execution","codepipeline_stop_execution","confluence_add_label","confluence_create_blogpost","confluence_create_comment","confluence_create_page","confluence_create_page_property","confluence_create_space","confluence_create_space_property","confluence_delete_attachment","confluence_delete_blogpost","confluence_delete_comment","confluence_delete_label","confluence_delete_page","confluence_delete_page_property","confluence_delete_space","confluence_delete_space_property","confluence_get_blogpost","confluence_get_page_ancestors","confluence_get_page_children","confluence_get_page_descendants","confluence_get_page_version","confluence_get_pages_by_label","confluence_get_space","confluence_get_task","confluence_get_user","confluence_list_attachments","confluence_list_blogposts","confluence_list_blogposts_in_space","confluence_list_comments","confluence_list_labels","confluence_list_page_properties","confluence_list_page_versions","confluence_list_pages_in_space","confluence_list_space_labels","confluence_list_space_permissions","confluence_list_space_properties","confluence_list_spaces","confluence_list_tasks","confluence_retrieve","confluence_search","confluence_search_in_space","confluence_update","confluence_update_blogpost","confluence_update_comment","confluence_update_space","confluence_update_task","confluence_upload_attachment","context_dev_classify_naics","context_dev_classify_sic","context_dev_crawl","context_dev_extract","context_dev_extract_product","context_dev_extract_products","context_dev_get_brand","context_dev_get_brand_by_email","context_dev_get_brand_by_name","context_dev_get_brand_by_ticker","context_dev_identify_transaction","context_dev_map","context_dev_scrape_fonts","context_dev_scrape_html","context_dev_scrape_images","context_dev_scrape_markdown","context_dev_scrape_styleguide","context_dev_screenshot","context_dev_search","convex_action","convex_document_deltas","convex_list_documents","convex_list_tables","convex_mutation","convex_query","convex_run_function","crowdstrike_create_indicators","crowdstrike_delete_indicators","crowdstrike_delete_rtr_session","crowdstrike_execute_rtr_command","crowdstrike_get_alert_details","crowdstrike_get_case_details","crowdstrike_get_host_group_details","crowdstrike_get_indicator_details","crowdstrike_get_rtr_command_status","crowdstrike_get_sensor_aggregates","crowdstrike_get_sensor_details","crowdstrike_get_vulnerability_details","crowdstrike_init_rtr_session","crowdstrike_perform_host_action","crowdstrike_perform_host_group_action","crowdstrike_query_alerts","crowdstrike_query_cases","crowdstrike_query_host_groups","crowdstrike_query_indicators","crowdstrike_query_sensors","crowdstrike_query_vulnerabilities","crowdstrike_update_alerts","crowdstrike_update_indicators","crunchbase_autocomplete","crunchbase_get_acquisition","crunchbase_get_entity","crunchbase_get_entity_card","crunchbase_get_fields_metadata","crunchbase_get_funding_round","crunchbase_get_organization","crunchbase_get_person","crunchbase_list_deleted_entities","crunchbase_search_acquisitions","crunchbase_search_entities","crunchbase_search_funding_rounds","crunchbase_search_organizations","crunchbase_search_people","cursor_add_followup","cursor_add_followup_v2","cursor_delete_agent","cursor_delete_agent_v2","cursor_download_artifact","cursor_download_artifact_v2","cursor_get_agent","cursor_get_agent_v2","cursor_get_api_key_info","cursor_get_api_key_info_v2","cursor_get_conversation","cursor_get_conversation_v2","cursor_launch_agent","cursor_launch_agent_v2","cursor_list_agents","cursor_list_agents_v2","cursor_list_artifacts","cursor_list_artifacts_v2","cursor_list_models","cursor_list_models_v2","cursor_list_repositories","cursor_list_repositories_v2","cursor_stop_agent","cursor_stop_agent_v2","dagster_delete_run","dagster_get_asset","dagster_get_run","dagster_get_run_logs","dagster_launch_run","dagster_list_assets","dagster_list_jobs","dagster_list_runs","dagster_list_schedules","dagster_list_sensors","dagster_materialize_assets","dagster_reexecute_run","dagster_report_asset_materialization","dagster_start_schedule","dagster_start_sensor","dagster_stop_schedule","dagster_stop_sensor","dagster_terminate_run","dagster_wipe_asset","databricks_cancel_all_runs","databricks_cancel_run","databricks_cancel_statement","databricks_create_cluster","databricks_create_job","databricks_create_warehouse","databricks_delete_cluster","databricks_delete_job","databricks_delete_run","databricks_delete_warehouse","databricks_edit_cluster","databricks_edit_warehouse","databricks_execute_sql","databricks_genie_agent_ask","databricks_genie_agent_cancel","databricks_genie_agent_list_items","databricks_genie_ask","databricks_genie_create_space","databricks_genie_delete_conversation","databricks_genie_delete_message","databricks_genie_delete_space","databricks_genie_download_visualization","databricks_genie_execute_query","databricks_genie_get_message","databricks_genie_get_query_result","databricks_genie_get_space","databricks_genie_list_conversations","databricks_genie_list_messages","databricks_genie_list_spaces","databricks_genie_send_feedback","databricks_genie_update_space","databricks_get_cluster","databricks_get_job","databricks_get_run","databricks_get_run_output","databricks_get_statement","databricks_get_statement_chunk","databricks_get_warehouse","databricks_list_clusters","databricks_list_jobs","databricks_list_runs","databricks_list_warehouses","databricks_repair_run","databricks_reset_job","databricks_resize_cluster","databricks_restart_cluster","databricks_run_job","databricks_start_cluster","databricks_start_warehouse","databricks_stop_warehouse","databricks_submit_run","databricks_terminate_cluster","databricks_update_job","datadog_add_incident_todo","datadog_cancel_downtime","datadog_create_dashboard","datadog_create_downtime","datadog_create_event","datadog_create_incident","datadog_create_monitor","datadog_create_slo","datadog_delete_dashboard","datadog_delete_slo","datadog_get_browser_synthetics_results","datadog_get_dashboard","datadog_get_incident","datadog_get_monitor","datadog_get_security_signal","datadog_get_slo","datadog_get_slo_history","datadog_get_synthetics_results","datadog_get_synthetics_test","datadog_list_dashboards","datadog_list_downtimes","datadog_list_incidents","datadog_list_monitors","datadog_list_security_rules","datadog_list_security_signals","datadog_list_services","datadog_list_slos","datadog_list_synthetics_tests","datadog_mute_monitor","datadog_query_logs","datadog_query_timeseries","datadog_search_spans","datadog_send_logs","datadog_submit_metrics","datadog_trigger_synthetics_tests","datadog_unmute_monitor","datadog_update_incident","datadog_update_security_signal_assignee","datadog_update_security_signal_state","datadog_update_slo","datadog_update_synthetics_status","datagma_enrich_company","datagma_enrich_person","datagma_find_email","datagma_find_phone","datagma_get_credits","daytona_create_sandbox","daytona_delete_sandbox","daytona_download_file","daytona_execute_command","daytona_get_sandbox","daytona_git_clone","daytona_list_files","daytona_list_sandboxes","daytona_run_code","daytona_start_sandbox","daytona_stop_sandbox","daytona_upload_file","deployed_block_executor","deployments_deploy","deployments_get_version","deployments_list_versions","deployments_promote","deployments_undeploy","devin_append_session_tags","devin_archive_session","devin_create_session","devin_get_session","devin_get_session_tags","devin_list_session_attachments","devin_list_session_messages","devin_list_sessions","devin_replace_session_tags","devin_send_message","devin_terminate_session","discord_add_reaction","discord_archive_thread","discord_assign_role","discord_ban_member","discord_bulk_delete_messages","discord_create_channel","discord_create_invite","discord_create_role","discord_create_thread","discord_create_webhook","discord_delete_channel","discord_delete_invite","discord_delete_message","discord_delete_role","discord_delete_webhook","discord_edit_message","discord_execute_webhook","discord_get_channel","discord_get_invite","discord_get_member","discord_get_messages","discord_get_pinned_messages","discord_get_server","discord_get_user","discord_get_webhook","discord_join_thread","discord_kick_member","discord_leave_thread","discord_list_channels","discord_list_roles","discord_pin_message","discord_remove_reaction","discord_remove_role","discord_send_message","discord_unban_member","discord_unpin_message","discord_update_channel","discord_update_member","discord_update_role","docusign_create_from_template","docusign_download_document","docusign_get_envelope","docusign_list_envelopes","docusign_list_recipients","docusign_list_templates","docusign_send_envelope","docusign_void_envelope","downdetector_get_company","downdetector_get_company_attribution","downdetector_get_company_baseline","downdetector_get_company_events","downdetector_get_company_incidents","downdetector_get_company_indicators","downdetector_get_company_last_15","downdetector_get_company_status","downdetector_get_provider","downdetector_get_reports","downdetector_get_site_companies","downdetector_list_categories","downdetector_list_incidents","downdetector_list_sites","downdetector_search_companies","dropbox_copy","dropbox_create_folder","dropbox_create_shared_link","dropbox_delete","dropbox_download","dropbox_download_v2","dropbox_get_metadata","dropbox_list_folder","dropbox_list_revisions","dropbox_list_shared_links","dropbox_move","dropbox_restore","dropbox_search","dropbox_upload","dropcontact_enrich_contact","dspy_chain_of_thought","dspy_predict","dspy_react","dub_bulk_create_links","dub_bulk_delete_links","dub_bulk_update_links","dub_create_link","dub_create_tag","dub_delete_link","dub_get_analytics","dub_get_events","dub_get_link","dub_get_links_count","dub_get_qr_code","dub_get_qr_code_v2","dub_list_domains","dub_list_folders","dub_list_links","dub_list_tags","dub_update_link","dub_upsert_link","duckduckgo_search","dynamodb_delete","dynamodb_get","dynamodb_introspect","dynamodb_put","dynamodb_query","dynamodb_scan","dynamodb_update","dynatrace_add_problem_comment","dynatrace_add_tags","dynatrace_close_problem","dynatrace_create_settings_object","dynatrace_create_slo","dynatrace_delete_problem_comment","dynatrace_delete_settings_object","dynatrace_delete_slo","dynatrace_delete_tag","dynatrace_execute_synthetic_monitors","dynatrace_get_attack","dynatrace_get_audit_logs","dynatrace_get_entity","dynatrace_get_event","dynatrace_get_metric","dynatrace_get_problem","dynatrace_get_problem_comment","dynatrace_get_security_problem","dynatrace_get_settings_object","dynatrace_get_slo","dynatrace_get_synthetic_batch","dynatrace_ingest_event","dynatrace_ingest_logs","dynatrace_ingest_metrics","dynatrace_list_attacks","dynatrace_list_entities","dynatrace_list_entity_types","dynatrace_list_events","dynatrace_list_metrics","dynatrace_list_problem_comments","dynatrace_list_problems","dynatrace_list_remediation_items","dynatrace_list_security_problems","dynatrace_list_settings_objects","dynatrace_list_settings_schemas","dynatrace_list_slos","dynatrace_list_synthetic_monitors","dynatrace_list_tags","dynatrace_mute_security_problem","dynatrace_mute_security_problems","dynatrace_query_metrics","dynatrace_search_logs","dynatrace_unmute_security_problem","dynatrace_unmute_security_problems","dynatrace_update_problem_comment","dynatrace_update_settings_object","dynatrace_update_slo","ec2_allocate_address","ec2_associate_address","ec2_authorize_security_group_ingress","ec2_create_snapshot","ec2_create_tags","ec2_delete_snapshot","ec2_delete_tags","ec2_delete_volume","ec2_describe_addresses","ec2_describe_availability_zones","ec2_describe_images","ec2_describe_instance_status","ec2_describe_instances","ec2_describe_internet_gateways","ec2_describe_key_pairs","ec2_describe_nat_gateways","ec2_describe_network_acls","ec2_describe_network_interfaces","ec2_describe_regions","ec2_describe_route_tables","ec2_describe_security_groups","ec2_describe_snapshots","ec2_describe_subnets","ec2_describe_volumes","ec2_describe_vpc_endpoints","ec2_describe_vpc_peering_connections","ec2_describe_vpcs","ec2_disassociate_address","ec2_reboot_instances","ec2_release_address","ec2_revoke_security_group_ingress","ec2_start_instances","ec2_stop_instances","ec2_terminate_instances","elasticsearch_bulk","elasticsearch_cluster_health","elasticsearch_cluster_stats","elasticsearch_count","elasticsearch_create_index","elasticsearch_delete_document","elasticsearch_delete_index","elasticsearch_get_document","elasticsearch_get_index","elasticsearch_index_document","elasticsearch_list_indices","elasticsearch_search","elasticsearch_update_document","elevenlabs_audio_isolation","elevenlabs_edit_voice_settings","elevenlabs_get_user","elevenlabs_get_voice","elevenlabs_get_voice_settings","elevenlabs_list_models","elevenlabs_list_voices","elevenlabs_sound_effects","elevenlabs_speech_to_speech","elevenlabs_tts","emailbison_attach_leads_to_campaign","emailbison_attach_tags_to_leads","emailbison_create_campaign","emailbison_create_lead","emailbison_create_tag","emailbison_get_lead","emailbison_list_campaigns","emailbison_list_leads","emailbison_list_replies","emailbison_list_tags","emailbison_update_campaign","emailbison_update_campaign_status","emailbison_update_lead","embeddings_cohere","embeddings_gemini","embeddings_mistral","embeddings_ollama","embeddings_openai","embeddings_openrouter","enrich_check_credits","enrich_company_funding","enrich_company_lookup","enrich_company_revenue","enrich_disposable_email_check","enrich_email_to_ip","enrich_email_to_person_lite","enrich_email_to_phone","enrich_email_to_profile","enrich_find_email","enrich_get_post_details","enrich_ip_to_company","enrich_linkedin_profile","enrich_linkedin_to_personal_email","enrich_linkedin_to_work_email","enrich_phone_finder","enrich_reverse_hash_lookup","enrich_sales_pointer_people","enrich_search_company","enrich_search_company_activities","enrich_search_company_employees","enrich_search_jobs","enrich_search_logo","enrich_search_people","enrich_search_people_activities","enrich_search_post_comments","enrich_search_post_comments_by_url","enrich_search_post_reactions","enrich_search_post_reactions_by_url","enrich_search_posts","enrich_search_similar_companies","enrich_verify_email","enrichment_run","enrow_find_email","enrow_verify_email","exa_agent","exa_answer","exa_find_similar_links","exa_get_contents","exa_search","extend_parser","extend_parser_v2","fathom_get_summary","fathom_get_transcript","fathom_list_meeting_types","fathom_list_meetings","fathom_list_team_members","fathom_list_teams","file_append","file_compress","file_create_folder","file_decompress","file_delete_folder","file_edit","file_fetch","file_get","file_get_content","file_list","file_manage_sharing","file_move","file_parser","file_parser_v2","file_parser_v3","file_read","file_restore_folder","file_search","file_update_folder","file_write","findymail_find_email_from_linkedin","findymail_find_email_from_name","findymail_find_emails_by_domain","findymail_find_employees","findymail_find_phone","findymail_get_company","findymail_get_credits","findymail_lookup_technologies","findymail_reverse_email_lookup","findymail_search_technologies","findymail_verify_email","firecrawl_agent","firecrawl_batch_scrape","firecrawl_batch_scrape_status","firecrawl_cancel_crawl","firecrawl_crawl","firecrawl_crawl_status","firecrawl_credit_usage","firecrawl_enrich","firecrawl_extract","firecrawl_extract_status","firecrawl_map","firecrawl_parse","firecrawl_scrape","firecrawl_search","fireflies_add_to_live_meeting","fireflies_create_bite","fireflies_delete_transcript","fireflies_get_transcript","fireflies_get_user","fireflies_list_bites","fireflies_list_contacts","fireflies_list_transcripts","fireflies_list_users","fireflies_upload_audio","fivetran_cancel_transformation","fivetran_create_connect_card","fivetran_create_connection","fivetran_create_destination","fivetran_create_group","fivetran_delete_connection","fivetran_get_account_info","fivetran_get_connection","fivetran_get_connection_schema_config","fivetran_get_connection_warnings","fivetran_get_connector_type","fivetran_get_destination","fivetran_get_group","fivetran_get_sync_history","fivetran_get_transformation","fivetran_list_connections","fivetran_list_connector_types","fivetran_list_destinations","fivetran_list_groups","fivetran_list_transformations","fivetran_list_users","fivetran_reload_connection_schema","fivetran_resync_connection","fivetran_resync_tables","fivetran_run_connection_setup_tests","fivetran_run_destination_setup_tests","fivetran_run_transformation","fivetran_sync_connection","fivetran_update_connection","fivetran_update_connection_schema","fivetran_update_connection_table","fivetran_update_schema_config","flint_create_task","flint_generate_pages","flint_get_task","flow_add_link","flow_create_branch","flow_create_comment","flow_create_entities","flow_create_entity","flow_delete_comment","flow_delete_entity","flow_filter_entities","flow_get_comment","flow_get_data_model","flow_get_entities","flow_get_entity_history","flow_get_project","flow_list_branches","flow_list_comments","flow_list_entities","flow_list_projects","flow_remove_link","flow_restore_entity","flow_search_entities","flow_set_value","flow_set_values","flow_update_comment","function_execute","gamma_check_status","gamma_generate","gamma_generate_from_template","gamma_list_folders","gamma_list_themes","gauge_add_tags_to_prompts","gauge_bulk_update_prompts","gauge_create_prompts","gauge_create_tag","gauge_create_topic","gauge_delete_tag","gauge_get_answer","gauge_get_category","gauge_get_current_credential","gauge_get_organization","gauge_get_prompt","gauge_get_query_metadata","gauge_get_tag","gauge_get_topic","gauge_list_answers","gauge_list_brands","gauge_list_categories","gauge_list_citations","gauge_list_organizations","gauge_list_prompts","gauge_list_tags","gauge_list_topics","gauge_query","gauge_remove_tags_from_prompts","gauge_rename_topic","gauge_update_prompt","gauge_update_tag","github_add_assignees","github_add_assignees_v2","github_add_labels","github_add_labels_v2","github_cancel_workflow_run","github_cancel_workflow_run_v2","github_check_star","github_check_star_v2","github_close_issue","github_close_issue_v2","github_close_pr","github_close_pr_v2","github_comment","github_comment_v2","github_compare_commits","github_compare_commits_v2","github_create_branch","github_create_branch_v2","github_create_comment_reaction","github_create_comment_reaction_v2","github_create_file","github_create_file_v2","github_create_gist","github_create_gist_v2","github_create_issue","github_create_issue_reaction","github_create_issue_reaction_v2","github_create_issue_v2","github_create_milestone","github_create_milestone_v2","github_create_pr","github_create_pr_review","github_create_pr_review_v2","github_create_pr_v2","github_create_project","github_create_project_v2","github_create_release","github_create_release_v2","github_delete_branch","github_delete_branch_v2","github_delete_comment","github_delete_comment_reaction","github_delete_comment_reaction_v2","github_delete_comment_v2","github_delete_file","github_delete_file_v2","github_delete_gist","github_delete_gist_v2","github_delete_issue_reaction","github_delete_issue_reaction_v2","github_delete_milestone","github_delete_milestone_v2","github_delete_project","github_delete_project_v2","github_delete_release","github_delete_release_v2","github_fork_gist","github_fork_gist_v2","github_fork_repo","github_fork_repo_v2","github_get_branch","github_get_branch_protection","github_get_branch_protection_v2","github_get_branch_v2","github_get_commit","github_get_commit_v2","github_get_file_content","github_get_file_content_v2","github_get_gist","github_get_gist_v2","github_get_issue","github_get_issue_v2","github_get_latest_release","github_get_latest_release_v2","github_get_milestone","github_get_milestone_v2","github_get_pr_files","github_get_pr_files_v2","github_get_project","github_get_project_v2","github_get_readme","github_get_readme_v2","github_get_release","github_get_release_v2","github_get_tree","github_get_tree_v2","github_get_workflow","github_get_workflow_run","github_get_workflow_run_v2","github_get_workflow_v2","github_issue_comment","github_issue_comment_v2","github_job_logs","github_latest_commit","github_latest_commit_v2","github_list_branches","github_list_branches_v2","github_list_commits","github_list_commits_v2","github_list_forks","github_list_forks_v2","github_list_gists","github_list_gists_v2","github_list_issue_comments","github_list_issue_comments_v2","github_list_issues","github_list_issues_v2","github_list_milestones","github_list_milestones_v2","github_list_pr_comments","github_list_pr_comments_v2","github_list_projects","github_list_projects_v2","github_list_prs","github_list_prs_v2","github_list_releases","github_list_releases_v2","github_list_review_threads","github_list_stargazers","github_list_stargazers_v2","github_list_tags","github_list_tags_v2","github_list_workflow_runs","github_list_workflow_runs_v2","github_list_workflows","github_list_workflows_v2","github_merge_pr","github_merge_pr_v2","github_pr","github_pr_v2","github_remove_label","github_remove_label_v2","github_reply_review_thread","github_repo_info","github_repo_info_v2","github_request_reviewers","github_request_reviewers_v2","github_rerun_workflow","github_rerun_workflow_v2","github_resolve_review_thread","github_search_code","github_search_code_v2","github_search_commits","github_search_commits_v2","github_search_issues","github_search_issues_v2","github_search_repos","github_search_repos_v2","github_search_users","github_search_users_v2","github_star_gist","github_star_gist_v2","github_star_repo","github_star_repo_v2","github_status_check_rollup","github_trigger_workflow","github_trigger_workflow_v2","github_unstar_gist","github_unstar_gist_v2","github_unstar_repo","github_unstar_repo_v2","github_update_branch_protection","github_update_branch_protection_v2","github_update_comment","github_update_comment_v2","github_update_file","github_update_file_v2","github_update_gist","github_update_gist_v2","github_update_issue","github_update_issue_v2","github_update_milestone","github_update_milestone_v2","github_update_pr","github_update_pr_v2","github_update_project","github_update_project_v2","github_update_release","github_update_release_v2","gitlab_activate_user","gitlab_add_member","gitlab_add_saml_group_link","gitlab_approve_access_request","gitlab_approve_merge_request","gitlab_approve_user","gitlab_ban_user","gitlab_block_user","gitlab_cancel_pipeline","gitlab_compare_branches","gitlab_create_branch","gitlab_create_file","gitlab_create_issue","gitlab_create_issue_note","gitlab_create_merge_request","gitlab_create_merge_request_note","gitlab_create_pipeline","gitlab_create_release","gitlab_create_user","gitlab_deactivate_user","gitlab_delete_branch","gitlab_delete_issue","gitlab_delete_saml_group_link","gitlab_delete_user","gitlab_delete_user_identity","gitlab_deny_access_request","gitlab_get_file","gitlab_get_group","gitlab_get_issue","gitlab_get_job_log","gitlab_get_merge_request","gitlab_get_merge_request_changes","gitlab_get_pipeline","gitlab_get_project","gitlab_invite_member","gitlab_list_access_requests","gitlab_list_branches","gitlab_list_commits","gitlab_list_groups","gitlab_list_invitations","gitlab_list_issues","gitlab_list_members","gitlab_list_merge_requests","gitlab_list_pipeline_jobs","gitlab_list_pipelines","gitlab_list_projects","gitlab_list_releases","gitlab_list_repository_tree","gitlab_list_saml_group_links","gitlab_list_user_memberships","gitlab_merge_merge_request","gitlab_play_job","gitlab_reject_user","gitlab_remove_member","gitlab_retry_pipeline","gitlab_revoke_invitation","gitlab_search_users","gitlab_unban_user","gitlab_unblock_user","gitlab_update_file","gitlab_update_invitation","gitlab_update_issue","gitlab_update_member","gitlab_update_merge_request","gitlab_update_user","gmail_add_label","gmail_add_label_v2","gmail_archive","gmail_archive_v2","gmail_create_label_v2","gmail_delete","gmail_delete_draft_v2","gmail_delete_label_v2","gmail_delete_v2","gmail_draft","gmail_draft_v2","gmail_edit_draft_v2","gmail_get_draft_v2","gmail_get_thread_v2","gmail_list_drafts_v2","gmail_list_labels_v2","gmail_list_threads_v2","gmail_mark_read","gmail_mark_read_v2","gmail_mark_unread","gmail_mark_unread_v2","gmail_move","gmail_move_v2","gmail_read","gmail_read_v2","gmail_remove_label","gmail_remove_label_v2","gmail_search","gmail_search_v2","gmail_send","gmail_send_v2","gmail_trash_thread_v2","gmail_unarchive","gmail_unarchive_v2","gmail_untrash_thread_v2","gmail_update_label_v2","gong_aggregate_activity","gong_aggregate_by_period","gong_answered_scorecards","gong_ask_anything","gong_assign_flow_prospects","gong_create_call","gong_day_by_day_activity","gong_get_brief","gong_get_call","gong_get_call_transcript","gong_get_coaching","gong_get_extensive_calls","gong_get_folder_content","gong_get_logs","gong_get_prospect_flows","gong_get_user","gong_interaction_stats","gong_list_calls","gong_list_flows","gong_list_library_folders","gong_list_scorecards","gong_list_trackers","gong_list_users","gong_list_workspaces","gong_lookup_email","gong_lookup_phone","gong_purge_email_address","gong_purge_phone_number","gong_unassign_flow_prospects","google_ads_ad_performance","google_ads_campaign_performance","google_ads_list_ad_groups","google_ads_list_campaigns","google_ads_list_customers","google_ads_search","google_appsheet_add_rows","google_appsheet_delete_rows","google_appsheet_edit_rows","google_appsheet_find_rows","google_bigquery_create_dataset","google_bigquery_create_table","google_bigquery_delete_dataset","google_bigquery_delete_table","google_bigquery_get_query_results","google_bigquery_get_table","google_bigquery_insert_rows","google_bigquery_list_datasets","google_bigquery_list_table_data","google_bigquery_list_tables","google_bigquery_query","google_books_volume_details","google_books_volume_search","google_calendar_create","google_calendar_create_calendar","google_calendar_create_calendar_v2","google_calendar_create_v2","google_calendar_delete","google_calendar_delete_calendar","google_calendar_delete_calendar_v2","google_calendar_delete_v2","google_calendar_freebusy","google_calendar_freebusy_v2","google_calendar_get","google_calendar_get_v2","google_calendar_instances","google_calendar_instances_v2","google_calendar_invite","google_calendar_invite_v2","google_calendar_list","google_calendar_list_acl","google_calendar_list_acl_v2","google_calendar_list_calendars","google_calendar_list_calendars_v2","google_calendar_list_v2","google_calendar_move","google_calendar_move_v2","google_calendar_quick_add","google_calendar_quick_add_v2","google_calendar_respond","google_calendar_respond_v2","google_calendar_share_calendar","google_calendar_share_calendar_v2","google_calendar_unshare_calendar","google_calendar_unshare_calendar_v2","google_calendar_update","google_calendar_update_acl","google_calendar_update_acl_v2","google_calendar_update_calendar","google_calendar_update_calendar_v2","google_calendar_update_v2","google_contacts_create","google_contacts_delete","google_contacts_get","google_contacts_list","google_contacts_search","google_contacts_update","google_docs_create","google_docs_create_named_range","google_docs_create_paragraph_bullets","google_docs_delete_content_range","google_docs_delete_named_range","google_docs_delete_paragraph_bullets","google_docs_insert_image","google_docs_insert_page_break","google_docs_insert_table","google_docs_insert_text","google_docs_read","google_docs_replace_text","google_docs_update_paragraph_style","google_docs_update_text_style","google_docs_write","google_drive_copy","google_drive_create_comment","google_drive_create_folder","google_drive_delete","google_drive_delete_comment","google_drive_download","google_drive_export","google_drive_get_about","google_drive_get_content","google_drive_get_file","google_drive_get_revision","google_drive_list","google_drive_list_comments","google_drive_list_permissions","google_drive_list_revisions","google_drive_move","google_drive_search","google_drive_share","google_drive_trash","google_drive_unshare","google_drive_untrash","google_drive_update","google_drive_upload","google_forms_batch_update","google_forms_create_form","google_forms_create_watch","google_forms_delete_watch","google_forms_get_form","google_forms_get_responses","google_forms_list_watches","google_forms_renew_watch","google_forms_set_publish_settings","google_groups_add_alias","google_groups_add_member","google_groups_create_group","google_groups_delete_group","google_groups_get_group","google_groups_get_member","google_groups_get_settings","google_groups_has_member","google_groups_list_aliases","google_groups_list_groups","google_groups_list_members","google_groups_remove_alias","google_groups_remove_member","google_groups_update_group","google_groups_update_member","google_groups_update_settings","google_maps_air_quality","google_maps_directions","google_maps_distance_matrix","google_maps_elevation","google_maps_geocode","google_maps_geolocate","google_maps_place_details","google_maps_places_nearby","google_maps_places_search","google_maps_pollen","google_maps_reverse_geocode","google_maps_snap_to_roads","google_maps_solar","google_maps_speed_limits","google_maps_timezone","google_maps_validate_address","google_meet_create_space","google_meet_end_conference","google_meet_get_conference_record","google_meet_get_space","google_meet_list_conference_records","google_meet_list_participants","google_pagespeed_analyze","google_search","google_sheets_append","google_sheets_append_v2","google_sheets_batch_clear_v2","google_sheets_batch_get_v2","google_sheets_batch_update_v2","google_sheets_clear_v2","google_sheets_copy_sheet_v2","google_sheets_create_spreadsheet_v2","google_sheets_delete_rows_v2","google_sheets_delete_sheet_v2","google_sheets_delete_spreadsheet_v2","google_sheets_get_spreadsheet_v2","google_sheets_read","google_sheets_read_v2","google_sheets_update","google_sheets_update_v2","google_sheets_write","google_sheets_write_v2","google_slides_add_image","google_slides_add_slide","google_slides_batch_update","google_slides_copy_presentation","google_slides_create","google_slides_create_line","google_slides_create_paragraph_bullets","google_slides_create_shape","google_slides_create_sheets_chart","google_slides_create_table","google_slides_create_video","google_slides_delete_object","google_slides_delete_paragraph_bullets","google_slides_delete_table_column","google_slides_delete_table_row","google_slides_delete_text","google_slides_duplicate_object","google_slides_export_presentation","google_slides_get_page","google_slides_get_thumbnail","google_slides_group_objects","google_slides_insert_table_columns","google_slides_insert_table_rows","google_slides_insert_text","google_slides_merge_table_cells","google_slides_read","google_slides_refresh_sheets_chart","google_slides_replace_all_shapes_with_image","google_slides_replace_all_shapes_with_sheets_chart","google_slides_replace_all_text","google_slides_replace_image","google_slides_reroute_line","google_slides_ungroup_objects","google_slides_unmerge_table_cells","google_slides_update_image_properties","google_slides_update_line_category","google_slides_update_line_properties","google_slides_update_page_element_alt_text","google_slides_update_page_element_transform","google_slides_update_page_elements_z_order","google_slides_update_page_properties","google_slides_update_paragraph_style","google_slides_update_shape_properties","google_slides_update_slide_properties","google_slides_update_slides_position","google_slides_update_table_border_properties","google_slides_update_table_cell_properties","google_slides_update_table_column_properties","google_slides_update_table_row_properties","google_slides_update_text_style","google_slides_update_video_properties","google_slides_write","google_tasks_create","google_tasks_delete","google_tasks_get","google_tasks_list","google_tasks_list_task_lists","google_tasks_update","google_translate_detect","google_translate_text","google_vault_add_held_accounts","google_vault_add_matters_permissions","google_vault_close_matters","google_vault_create_matters","google_vault_create_matters_export","google_vault_create_matters_holds","google_vault_create_saved_query","google_vault_delete_matters","google_vault_delete_matters_export","google_vault_delete_matters_holds","google_vault_delete_saved_query","google_vault_download_export_file","google_vault_list_matters","google_vault_list_matters_export","google_vault_list_matters_holds","google_vault_list_saved_queries","google_vault_remove_held_accounts","google_vault_remove_matters_permissions","google_vault_reopen_matters","google_vault_undelete_matters","google_vault_update_matters","google_vault_update_matters_holds","grafana_check_data_source_health","grafana_create_alert_rule","grafana_create_annotation","grafana_create_contact_point","grafana_create_dashboard","grafana_create_folder","grafana_delete_alert_rule","grafana_delete_annotation","grafana_delete_contact_point","grafana_delete_dashboard","grafana_delete_folder","grafana_get_alert_rule","grafana_get_alert_rule_group","grafana_get_dashboard","grafana_get_data_source","grafana_get_folder","grafana_get_health","grafana_list_alert_rules","grafana_list_annotations","grafana_list_contact_points","grafana_list_dashboards","grafana_list_data_sources","grafana_list_folders","grafana_move_folder","grafana_query_data_source","grafana_update_alert_rule","grafana_update_annotation","grafana_update_contact_point","grafana_update_dashboard","grafana_update_folder","grain_create_hook","grain_create_hook_v2","grain_delete_hook","grain_delete_hook_v2","grain_get_recording","grain_get_transcript","grain_list_hooks","grain_list_hooks_v2","grain_list_meeting_types","grain_list_recordings","grain_list_teams","grain_list_views","granola_create_webhook_endpoint","granola_delete_webhook_endpoint","granola_get_note","granola_get_transcript","granola_list_audit_events","granola_list_folders","granola_list_notes","granola_list_webhook_endpoints","granola_update_webhook_endpoint","greenhouse_apply_candidate_tag_v2","greenhouse_convert_prospect_v2","greenhouse_create_application_v2","greenhouse_create_attachment_v2","greenhouse_create_candidate_tag_v2","greenhouse_create_candidate_v2","greenhouse_create_note_v2","greenhouse_delete_application_v2","greenhouse_delete_attachment_v2","greenhouse_delete_candidate_v2","greenhouse_get_application","greenhouse_get_application_v2","greenhouse_get_candidate","greenhouse_get_candidate_v2","greenhouse_get_job","greenhouse_get_job_v2","greenhouse_get_user","greenhouse_get_user_v2","greenhouse_hire_application_v2","greenhouse_list_application_stages_v2","greenhouse_list_applications","greenhouse_list_applications_v2","greenhouse_list_applied_candidate_tags_v2","greenhouse_list_attachments_v2","greenhouse_list_candidate_educations_v2","greenhouse_list_candidate_employments_v2","greenhouse_list_candidate_tags_v2","greenhouse_list_candidates","greenhouse_list_candidates_v2","greenhouse_list_close_reasons_v2","greenhouse_list_custom_field_options_v2","greenhouse_list_custom_fields_v2","greenhouse_list_departments","greenhouse_list_departments_v2","greenhouse_list_email_templates_v2","greenhouse_list_interviews_v2","greenhouse_list_job_hiring_managers_v2","greenhouse_list_job_interview_stages_v2","greenhouse_list_job_owners_v2","greenhouse_list_job_stages","greenhouse_list_jobs","greenhouse_list_jobs_v2","greenhouse_list_notes_v2","greenhouse_list_offers_v2","greenhouse_list_offices","greenhouse_list_offices_v2","greenhouse_list_openings_v2","greenhouse_list_prospect_pool_stages_v2","greenhouse_list_prospect_pools_v2","greenhouse_list_referrers_v2","greenhouse_list_rejection_reasons_v2","greenhouse_list_scorecards_v2","greenhouse_list_sources_v2","greenhouse_list_users","greenhouse_list_users_v2","greenhouse_move_application_v2","greenhouse_reject_application_v2","greenhouse_remove_candidate_tag_v2","greenhouse_unreject_application_v2","greenhouse_update_application_v2","greenhouse_update_candidate_v2","greptile_index_repo","greptile_query","greptile_search","greptile_status","guardrails_validate","harmonic_batch_get_people","harmonic_clear_people_saved_search_net_new_results","harmonic_enrich_person","harmonic_get_company_employees","harmonic_get_email_enrichment_job","harmonic_get_email_enrichment_usage","harmonic_get_enrichment_status","harmonic_get_people_saved_search_net_new_results","harmonic_get_people_saved_search_results","harmonic_get_person","harmonic_list_people_saved_searches","harmonic_search_people_scout","harmonic_submit_email_enrichment_job","helpscout_assign_conversation","helpscout_create_conversation","helpscout_create_customer","helpscout_create_customer_thread","helpscout_create_note","helpscout_create_reply","helpscout_delete_conversation","helpscout_get_conversation","helpscout_get_current_user","helpscout_get_customer","helpscout_get_organization","helpscout_get_saved_reply","helpscout_list_conversations","helpscout_list_custom_fields","helpscout_list_customer_properties","helpscout_list_customers","helpscout_list_folders","helpscout_list_mailboxes","helpscout_list_organizations","helpscout_list_saved_replies","helpscout_list_tags","helpscout_list_teams","helpscout_list_threads","helpscout_list_users","helpscout_list_workflows","helpscout_move_conversation","helpscout_run_workflow","helpscout_set_conversation_tags","helpscout_snooze_conversation","helpscout_unassign_conversation","helpscout_unsnooze_conversation","helpscout_update_conversation_customer","helpscout_update_conversation_status","helpscout_update_conversation_subject","helpscout_update_custom_fields","helpscout_update_customer","helpscout_update_customer_properties","hex_cancel_run","hex_create_collection","hex_create_group","hex_deactivate_user","hex_delete_group","hex_get_collection","hex_get_data_connection","hex_get_group","hex_get_project","hex_get_project_runs","hex_get_queried_tables","hex_get_run_status","hex_list_collections","hex_list_data_connections","hex_list_groups","hex_list_projects","hex_list_users","hex_run_project","hex_update_collection","hex_update_group","hex_update_project","higgsfield_cancel_request","higgsfield_create_character","higgsfield_delete_character","higgsfield_delete_request","higgsfield_generate","higgsfield_generate_soul_image","higgsfield_get_character","higgsfield_get_request_status","higgsfield_list_characters","http_request","hubspot_add_list_memberships","hubspot_create_appointment","hubspot_create_association","hubspot_create_company","hubspot_create_contact","hubspot_create_deal","hubspot_create_email","hubspot_create_line_item","hubspot_create_list","hubspot_create_note","hubspot_create_ticket","hubspot_delete_association","hubspot_delete_company","hubspot_delete_contact","hubspot_delete_deal","hubspot_delete_line_item","hubspot_delete_ticket","hubspot_get_appointment","hubspot_get_association_labels","hubspot_get_cart","hubspot_get_company","hubspot_get_contact","hubspot_get_deal","hubspot_get_email","hubspot_get_line_item","hubspot_get_list","hubspot_get_list_memberships","hubspot_get_marketing_event","hubspot_get_note","hubspot_get_properties","hubspot_get_quote","hubspot_get_ticket","hubspot_get_users","hubspot_list_appointments","hubspot_list_associations","hubspot_list_carts","hubspot_list_companies","hubspot_list_contacts","hubspot_list_deals","hubspot_list_emails","hubspot_list_line_items","hubspot_list_lists","hubspot_list_marketing_events","hubspot_list_notes","hubspot_list_owners","hubspot_list_quotes","hubspot_list_tickets","hubspot_remove_list_memberships","hubspot_search_companies","hubspot_search_contacts","hubspot_search_deals","hubspot_search_emails","hubspot_search_line_items","hubspot_search_notes","hubspot_search_quotes","hubspot_search_tickets","hubspot_update_appointment","hubspot_update_company","hubspot_update_contact","hubspot_update_deal","hubspot_update_line_item","hubspot_update_ticket","huggingface_chat","hunter_companies_find","hunter_discover","hunter_domain_search","hunter_email_count","hunter_email_finder","hunter_email_verifier","iam_add_user_to_group","iam_attach_role_policy","iam_attach_user_policy","iam_create_access_key","iam_create_role","iam_create_user","iam_delete_access_key","iam_delete_role","iam_delete_user","iam_detach_role_policy","iam_detach_user_policy","iam_get_policy","iam_get_role","iam_get_user","iam_list_access_keys","iam_list_attached_role_policies","iam_list_attached_user_policies","iam_list_groups","iam_list_policies","iam_list_roles","iam_list_users","iam_remove_user_from_group","iam_simulate_principal_policy","iam_update_access_key","icypeas_find_email","icypeas_verify_email","identity_center_check_assignment_deletion_status","identity_center_check_assignment_status","identity_center_create_account_assignment","identity_center_delete_account_assignment","identity_center_describe_account","identity_center_describe_group","identity_center_describe_user","identity_center_get_group","identity_center_get_user","identity_center_list_account_assignments","identity_center_list_accounts","identity_center_list_assignments_for_account","identity_center_list_group_memberships","identity_center_list_groups","identity_center_list_instances","identity_center_list_permission_sets","image_generate","incidentio_actions_create","incidentio_actions_list","incidentio_actions_show","incidentio_actions_update","incidentio_alert_events_create","incidentio_alerts_list","incidentio_alerts_resolve","incidentio_alerts_show","incidentio_catalog_entries_list","incidentio_catalog_types_list","incidentio_custom_fields_create","incidentio_custom_fields_delete","incidentio_custom_fields_list","incidentio_custom_fields_show","incidentio_custom_fields_update","incidentio_escalation_paths_create","incidentio_escalation_paths_delete","incidentio_escalation_paths_list","incidentio_escalation_paths_show","incidentio_escalation_paths_update","incidentio_escalations_cancel","incidentio_escalations_create","incidentio_escalations_list","incidentio_escalations_show","incidentio_follow_ups_create","incidentio_follow_ups_list","incidentio_follow_ups_show","incidentio_follow_ups_update","incidentio_incident_alerts_list","incidentio_incident_memberships_create","incidentio_incident_memberships_revoke","incidentio_incident_participants_list","incidentio_incident_roles_create","incidentio_incident_roles_delete","incidentio_incident_roles_list","incidentio_incident_roles_show","incidentio_incident_roles_update","incidentio_incident_statuses_list","incidentio_incident_timestamps_list","incidentio_incident_timestamps_show","incidentio_incident_types_list","incidentio_incident_updates_list","incidentio_incidents_create","incidentio_incidents_list","incidentio_incidents_show","incidentio_incidents_update","incidentio_on_call_now","incidentio_schedule_entries_list","incidentio_schedule_overrides_create","incidentio_schedule_overrides_list","incidentio_schedules_create","incidentio_schedules_delete","incidentio_schedules_list","incidentio_schedules_show","incidentio_schedules_update","incidentio_severities_list","incidentio_teams_list","incidentio_teams_show","incidentio_users_list","incidentio_users_show","incidentio_workflows_create","incidentio_workflows_delete","incidentio_workflows_list","incidentio_workflows_show","incidentio_workflows_update","infisical_create_secret","infisical_delete_secret","infisical_get_secret","infisical_list_secrets","infisical_update_secret","instagram_delete_comment","instagram_download_media","instagram_get_account_insights","instagram_get_container_status","instagram_get_conversation_messages","instagram_get_media","instagram_get_media_insights","instagram_get_message","instagram_get_profile","instagram_get_publishing_limit","instagram_hide_comment","instagram_list_comments","instagram_list_conversations","instagram_list_media","instagram_list_stories","instagram_private_reply","instagram_publish_carousel","instagram_publish_image","instagram_publish_reel","instagram_publish_story","instagram_publish_video","instagram_reply_to_comment","instagram_send_text_message","instagram_set_comments_enabled","instantly_activate_campaign","instantly_create_campaign","instantly_create_lead","instantly_create_lead_list","instantly_delete_campaign","instantly_delete_leads","instantly_get_lead","instantly_list_campaigns","instantly_list_emails","instantly_list_lead_lists","instantly_list_leads","instantly_patch_campaign","instantly_patch_lead","instantly_pause_campaign","instantly_reply_to_email","instantly_update_lead_interest_status","intercom_assign_conversation_v2","intercom_attach_contact_to_company_v2","intercom_close_conversation_v2","intercom_create_company","intercom_create_company_v2","intercom_create_contact","intercom_create_contact_v2","intercom_create_event_v2","intercom_create_message","intercom_create_message_v2","intercom_create_note_v2","intercom_create_tag_v2","intercom_create_ticket","intercom_create_ticket_v2","intercom_delete_contact","intercom_delete_contact_v2","intercom_detach_contact_from_company_v2","intercom_get_company","intercom_get_company_v2","intercom_get_contact","intercom_get_contact_v2","intercom_get_conversation","intercom_get_conversation_v2","intercom_get_ticket","intercom_get_ticket_v2","intercom_list_admins_v2","intercom_list_companies","intercom_list_companies_v2","intercom_list_contacts","intercom_list_contacts_v2","intercom_list_conversations","intercom_list_conversations_v2","intercom_list_tags_v2","intercom_open_conversation_v2","intercom_reply_conversation","intercom_reply_conversation_v2","intercom_search_contacts","intercom_search_contacts_v2","intercom_search_conversations","intercom_search_conversations_v2","intercom_snooze_conversation_v2","intercom_tag_contact_v2","intercom_tag_conversation_v2","intercom_untag_contact_v2","intercom_update_contact","intercom_update_contact_v2","intercom_update_ticket_v2","intune_get_compliance_policy","intune_get_detected_app","intune_get_device","intune_get_device_configuration","intune_list_app_devices","intune_list_compliance_policies","intune_list_compliance_policy_device_statuses","intune_list_detected_apps","intune_list_device_configurations","intune_list_devices","intune_reboot_device","intune_remote_lock_device","intune_retire_device","intune_sync_device","jama_create_comment","jama_create_item","jama_create_relationship","jama_delete_item","jama_delete_relationship","jama_get_baseline","jama_get_current_user","jama_get_filter","jama_get_item","jama_get_item_type","jama_get_item_version","jama_get_project","jama_get_relationship","jama_get_test_cycle","jama_get_test_plan","jama_get_test_run","jama_list_baseline_items","jama_list_baselines","jama_list_downstream_items","jama_list_filter_results","jama_list_filters","jama_list_item_children","jama_list_item_comments","jama_list_item_types","jama_list_item_versions","jama_list_items","jama_list_pick_list_options","jama_list_pick_lists","jama_list_projects","jama_list_relationship_types","jama_list_relationships","jama_list_releases","jama_list_tags","jama_list_test_cycles","jama_list_test_plans","jama_list_test_runs","jama_list_upstream_items","jama_search_items","jama_update_item","jama_update_test_run","jina_read_url","jina_search","jira_add_attachment","jira_add_comment","jira_add_watcher","jira_add_worklog","jira_assign_issue","jira_bulk_read","jira_create_issue_link","jira_create_sprint","jira_delete_attachment","jira_delete_comment","jira_delete_issue","jira_delete_issue_link","jira_delete_worklog","jira_get_attachments","jira_get_comments","jira_get_fields","jira_get_project","jira_get_transitions","jira_get_users","jira_get_worklogs","jira_list_issue_types","jira_list_projects","jira_remove_watcher","jira_retrieve","jira_search_issues","jira_search_users","jira_transition_issue","jira_update","jira_update_comment","jira_update_worklog","jira_write","jotform_add_label_resources","jotform_clone_form","jotform_create_form","jotform_create_label","jotform_create_question","jotform_create_questions","jotform_create_report","jotform_create_submission","jotform_create_submissions","jotform_create_webhook","jotform_delete_form","jotform_delete_label","jotform_delete_question","jotform_delete_report","jotform_delete_submission","jotform_delete_webhook","jotform_get_form","jotform_get_form_properties","jotform_get_history","jotform_get_label","jotform_get_question","jotform_get_report","jotform_get_settings","jotform_get_submission","jotform_get_usage","jotform_get_user","jotform_list_form_files","jotform_list_form_reports","jotform_list_form_submissions","jotform_list_forms","jotform_list_label_resources","jotform_list_labels","jotform_list_questions","jotform_list_reports","jotform_list_submissions","jotform_list_subusers","jotform_list_webhooks","jotform_remove_label_resources","jotform_update_form_properties","jotform_update_label","jotform_update_question","jotform_update_settings","jotform_update_submission","jsm_add_comment","jsm_add_customer","jsm_add_organization","jsm_add_participants","jsm_answer_approval","jsm_attach_form","jsm_copy_forms","jsm_create_object","jsm_create_organization","jsm_create_request","jsm_delete_form","jsm_delete_object","jsm_externalise_form","jsm_get_approvals","jsm_get_comments","jsm_get_customers","jsm_get_form","jsm_get_form_answers","jsm_get_form_structure","jsm_get_form_templates","jsm_get_issue_forms","jsm_get_object","jsm_get_object_schema","jsm_get_object_type_attributes","jsm_get_organizations","jsm_get_participants","jsm_get_queues","jsm_get_request","jsm_get_request_type_fields","jsm_get_request_types","jsm_get_requests","jsm_get_service_desks","jsm_get_sla","jsm_get_transitions","jsm_internalise_form","jsm_list_object_schemas","jsm_list_object_types","jsm_reopen_form","jsm_save_form_answers","jsm_search_objects_aql","jsm_submit_form","jsm_transition_request","jsm_update_object","jupyter_copy_content","jupyter_create_file","jupyter_create_session","jupyter_delete_content","jupyter_delete_session","jupyter_get_content","jupyter_get_content_v2","jupyter_interrupt_kernel","jupyter_list_contents","jupyter_list_kernels","jupyter_list_kernelspecs","jupyter_list_sessions","jupyter_rename_content","jupyter_restart_kernel","jupyter_start_kernel","jupyter_stop_kernel","jupyter_upload_file","kalshi_amend_order","kalshi_amend_order_v2","kalshi_cancel_order","kalshi_cancel_order_v2","kalshi_create_order","kalshi_create_order_v2","kalshi_get_balance","kalshi_get_balance_v2","kalshi_get_candlesticks","kalshi_get_candlesticks_v2","kalshi_get_event","kalshi_get_event_candlesticks","kalshi_get_event_candlesticks_v2","kalshi_get_event_v2","kalshi_get_events","kalshi_get_events_v2","kalshi_get_exchange_announcements","kalshi_get_exchange_announcements_v2","kalshi_get_exchange_schedule","kalshi_get_exchange_schedule_v2","kalshi_get_exchange_status","kalshi_get_exchange_status_v2","kalshi_get_fills","kalshi_get_fills_v2","kalshi_get_market","kalshi_get_market_v2","kalshi_get_markets","kalshi_get_markets_v2","kalshi_get_order","kalshi_get_order_v2","kalshi_get_orderbook","kalshi_get_orderbook_v2","kalshi_get_orders","kalshi_get_orders_v2","kalshi_get_positions","kalshi_get_positions_v2","kalshi_get_series_by_ticker","kalshi_get_series_by_ticker_v2","kalshi_get_series_list","kalshi_get_series_list_v2","kalshi_get_settlements","kalshi_get_settlements_v2","kalshi_get_trades","kalshi_get_trades_v2","ketch_get_consent","ketch_get_subscriptions","ketch_invoke_right","ketch_set_consent","ketch_set_subscriptions","knowledge_create_document","knowledge_delete_chunk","knowledge_delete_document","knowledge_get_connector","knowledge_get_document","knowledge_list_chunks","knowledge_list_connectors","knowledge_list_documents","knowledge_list_tags","knowledge_search","knowledge_trigger_sync","knowledge_update_chunk","knowledge_upload_chunk","knowledge_upsert_document","lambda_add_permission","lambda_create_alias","lambda_create_event_source_mapping","lambda_create_function","lambda_create_function_url_config","lambda_delete_alias","lambda_delete_event_source_mapping","lambda_delete_function","lambda_delete_function_concurrency","lambda_delete_function_event_invoke_config","lambda_delete_function_url_config","lambda_delete_provisioned_concurrency_config","lambda_get_account_settings","lambda_get_alias","lambda_get_event_source_mapping","lambda_get_function","lambda_get_function_concurrency","lambda_get_function_configuration","lambda_get_function_event_invoke_config","lambda_get_function_recursion_config","lambda_get_function_url_config","lambda_get_layer_version","lambda_get_policy","lambda_get_provisioned_concurrency_config","lambda_get_runtime_management_config","lambda_invoke","lambda_list_aliases","lambda_list_event_source_mappings","lambda_list_function_event_invoke_configs","lambda_list_function_url_configs","lambda_list_functions","lambda_list_layer_versions","lambda_list_layers","lambda_list_provisioned_concurrency_configs","lambda_list_tags","lambda_list_versions_by_function","lambda_publish_version","lambda_put_function_concurrency","lambda_put_function_event_invoke_config","lambda_put_function_recursion_config","lambda_put_provisioned_concurrency_config","lambda_put_runtime_management_config","lambda_remove_permission","lambda_tag_resource","lambda_untag_resource","lambda_update_alias","lambda_update_event_source_mapping","lambda_update_function_code","lambda_update_function_configuration","lambda_update_function_url_config","langsmith_create_feedback","langsmith_create_run","langsmith_create_runs_batch","langsmith_get_run","langsmith_update_run","latex_compile","latex_get_package","latex_list_fonts","latex_search_packages","launchdarkly_create_flag","launchdarkly_delete_flag","launchdarkly_get_audit_log","launchdarkly_get_flag","launchdarkly_get_flag_status","launchdarkly_list_environments","launchdarkly_list_flags","launchdarkly_list_members","launchdarkly_list_projects","launchdarkly_list_segments","launchdarkly_toggle_flag","launchdarkly_update_flag","leadmagic_company_search","leadmagic_email_to_profile","leadmagic_find_email","leadmagic_find_mobile","leadmagic_get_credits","leadmagic_profile_search","leadmagic_profile_to_email","leadmagic_role_finder","leadmagic_validate_email","lemlist_get_activities","lemlist_get_lead","lemlist_send_email","linear_add_label_to_issue","linear_add_label_to_project","linear_archive_issue","linear_archive_label","linear_archive_project","linear_create_attachment","linear_create_comment","linear_create_customer","linear_create_customer_request","linear_create_customer_status","linear_create_customer_tier","linear_create_cycle","linear_create_favorite","linear_create_issue","linear_create_issue_relation","linear_create_label","linear_create_project","linear_create_project_label","linear_create_project_milestone","linear_create_project_status","linear_create_project_update","linear_create_workflow_state","linear_delete_attachment","linear_delete_comment","linear_delete_customer","linear_delete_customer_status","linear_delete_customer_tier","linear_delete_issue","linear_delete_issue_relation","linear_delete_project","linear_delete_project_label","linear_delete_project_milestone","linear_delete_project_status","linear_get_active_cycle","linear_get_customer","linear_get_cycle","linear_get_issue","linear_get_project","linear_get_viewer","linear_list_attachments","linear_list_comments","linear_list_customer_requests","linear_list_customer_statuses","linear_list_customer_tiers","linear_list_customers","linear_list_cycles","linear_list_favorites","linear_list_issue_relations","linear_list_labels","linear_list_notifications","linear_list_project_labels","linear_list_project_milestones","linear_list_project_statuses","linear_list_project_updates","linear_list_projects","linear_list_teams","linear_list_users","linear_list_workflow_states","linear_merge_customers","linear_read_issues","linear_remove_label_from_issue","linear_remove_label_from_project","linear_search_issues","linear_unarchive_issue","linear_update_attachment","linear_update_comment","linear_update_customer","linear_update_customer_request","linear_update_customer_status","linear_update_customer_tier","linear_update_issue","linear_update_label","linear_update_notification","linear_update_project","linear_update_project_label","linear_update_project_milestone","linear_update_project_status","linear_update_workflow_state","linkedin_get_profile","linkedin_share_post","linkup_search","linq_add_participant","linq_check_imessage","linq_check_rcs","linq_create_attachment","linq_create_chat","linq_create_contact_card","linq_create_webhook_subscription","linq_delete_attachment","linq_delete_message","linq_delete_webhook_subscription","linq_edit_message","linq_get_attachment","linq_get_chat","linq_get_contact_card","linq_get_message","linq_get_webhook_subscription","linq_leave_chat","linq_list_chats","linq_list_messages","linq_list_phone_numbers","linq_list_thread","linq_list_webhook_events","linq_list_webhook_subscriptions","linq_mark_chat_read","linq_react_to_message","linq_remove_participant","linq_send_message","linq_send_voice_memo","linq_share_contact_card","linq_start_typing","linq_stop_typing","linq_update_chat","linq_update_contact_card","linq_update_webhook_subscription","llm_chat","logfire_get_token_info","logfire_get_trace","logfire_query","logfire_search_records","logrocket_create_release","logrocket_get_audit_logs","logrocket_get_highlights","logrocket_identify_user","logrocket_list_exported_sessions","logrocket_request_highlights","logs_get","logs_get_execution","logs_get_run_details","logs_query","logs_query_runs","loops_check_contact_suppression","loops_create_contact","loops_create_contact_property","loops_delete_contact","loops_find_contact","loops_get_transactional_email","loops_list_contact_properties","loops_list_mailing_lists","loops_list_transactional_emails","loops_remove_contact_suppression","loops_send_event","loops_send_transactional_email","loops_update_contact","luma_add_guests","luma_cancel_event","luma_create_event","luma_get_event","luma_get_guest","luma_get_guests","luma_list_events","luma_lookup_event","luma_send_invites","luma_update_event","luma_update_guest_status","mailchimp_add_member","mailchimp_add_member_tags","mailchimp_add_or_update_member","mailchimp_add_segment_member","mailchimp_add_subscriber_to_automation","mailchimp_archive_member","mailchimp_create_audience","mailchimp_create_batch_operation","mailchimp_create_campaign","mailchimp_create_interest","mailchimp_create_interest_category","mailchimp_create_landing_page","mailchimp_create_merge_field","mailchimp_create_segment","mailchimp_create_template","mailchimp_delete_audience","mailchimp_delete_batch_operation","mailchimp_delete_campaign","mailchimp_delete_interest","mailchimp_delete_interest_category","mailchimp_delete_landing_page","mailchimp_delete_member","mailchimp_delete_merge_field","mailchimp_delete_segment","mailchimp_delete_template","mailchimp_get_audience","mailchimp_get_audiences","mailchimp_get_automation","mailchimp_get_automations","mailchimp_get_batch_operation","mailchimp_get_batch_operations","mailchimp_get_campaign","mailchimp_get_campaign_content","mailchimp_get_campaign_report","mailchimp_get_campaign_reports","mailchimp_get_campaigns","mailchimp_get_interest","mailchimp_get_interest_categories","mailchimp_get_interest_category","mailchimp_get_interests","mailchimp_get_landing_page","mailchimp_get_landing_pages","mailchimp_get_member","mailchimp_get_member_tags","mailchimp_get_members","mailchimp_get_merge_field","mailchimp_get_merge_fields","mailchimp_get_segment","mailchimp_get_segment_members","mailchimp_get_segments","mailchimp_get_template","mailchimp_get_templates","mailchimp_pause_automation","mailchimp_publish_landing_page","mailchimp_remove_member_tags","mailchimp_remove_segment_member","mailchimp_replicate_campaign","mailchimp_schedule_campaign","mailchimp_send_campaign","mailchimp_set_campaign_content","mailchimp_start_automation","mailchimp_unarchive_member","mailchimp_unpublish_landing_page","mailchimp_unschedule_campaign","mailchimp_update_audience","mailchimp_update_campaign","mailchimp_update_interest","mailchimp_update_interest_category","mailchimp_update_landing_page","mailchimp_update_member","mailchimp_update_merge_field","mailchimp_update_segment","mailchimp_update_template","mailgun_add_list_member","mailgun_create_mailing_list","mailgun_get_domain","mailgun_get_mailing_list","mailgun_get_message","mailgun_list_domains","mailgun_list_messages","mailgun_send_message","managed_agent_archive_session","managed_agent_create_session","managed_agent_delete_session","managed_agent_get_session","managed_agent_interrupt_session","managed_agent_list_events","managed_agent_respond_custom_tool","managed_agent_respond_tool_confirmation","managed_agent_run_session","managed_agent_send_message","managed_agent_update_session","manageengine_sdp_add_change_note","manageengine_sdp_add_problem_note","manageengine_sdp_add_request_note","manageengine_sdp_create_asset","manageengine_sdp_create_change","manageengine_sdp_create_problem","manageengine_sdp_create_request","manageengine_sdp_create_solution","manageengine_sdp_delete_asset","manageengine_sdp_delete_change","manageengine_sdp_delete_problem","manageengine_sdp_delete_request","manageengine_sdp_delete_solution","manageengine_sdp_get_asset","manageengine_sdp_get_change","manageengine_sdp_get_problem","manageengine_sdp_get_request","manageengine_sdp_get_solution","manageengine_sdp_list_assets","manageengine_sdp_list_change_notes","manageengine_sdp_list_changes","manageengine_sdp_list_problem_notes","manageengine_sdp_list_problems","manageengine_sdp_list_request_notes","manageengine_sdp_list_requests","manageengine_sdp_list_solutions","manageengine_sdp_update_asset","manageengine_sdp_update_change","manageengine_sdp_update_problem","manageengine_sdp_update_request","manageengine_sdp_update_solution","mcp_list_operations","mcp_run_operation","mem0_add_memories","mem0_get_memories","mem0_search_memories","memory_add","memory_delete","memory_get","memory_get_all","microsoft_ad_add_directory_role_member","microsoft_ad_add_group_member","microsoft_ad_add_user_app_role_assignment","microsoft_ad_assign_license","microsoft_ad_create_group","microsoft_ad_create_user","microsoft_ad_delete_group","microsoft_ad_delete_user","microsoft_ad_get_conditional_access_policy","microsoft_ad_get_device","microsoft_ad_get_group","microsoft_ad_get_user","microsoft_ad_list_authentication_methods","microsoft_ad_list_conditional_access_policies","microsoft_ad_list_devices","microsoft_ad_list_directory_audits","microsoft_ad_list_directory_role_members","microsoft_ad_list_directory_roles","microsoft_ad_list_group_members","microsoft_ad_list_groups","microsoft_ad_list_service_principal_app_role_assignments","microsoft_ad_list_service_principals","microsoft_ad_list_sign_ins","microsoft_ad_list_subscribed_skus","microsoft_ad_list_user_app_role_assignments","microsoft_ad_list_user_devices","microsoft_ad_list_user_licenses","microsoft_ad_list_user_memberships","microsoft_ad_list_users","microsoft_ad_remove_directory_role_member","microsoft_ad_remove_group_member","microsoft_ad_remove_user_app_role_assignment","microsoft_ad_reset_password","microsoft_ad_revoke_sign_in_sessions","microsoft_ad_set_password","microsoft_ad_update_group","microsoft_ad_update_user","microsoft_dataverse_associate","microsoft_dataverse_create_multiple","microsoft_dataverse_create_record","microsoft_dataverse_delete_record","microsoft_dataverse_disassociate","microsoft_dataverse_download_file","microsoft_dataverse_download_file_v2","microsoft_dataverse_execute_action","microsoft_dataverse_execute_function","microsoft_dataverse_fetchxml_query","microsoft_dataverse_get_entity_metadata","microsoft_dataverse_get_record","microsoft_dataverse_list_records","microsoft_dataverse_search","microsoft_dataverse_update_multiple","microsoft_dataverse_update_record","microsoft_dataverse_upload_file","microsoft_dataverse_upsert_record","microsoft_dataverse_whoami","microsoft_dynamics_365_close_case","microsoft_dynamics_365_close_opportunity","microsoft_dynamics_365_create_record","microsoft_dynamics_365_get_record","microsoft_dynamics_365_list_records","microsoft_dynamics_365_qualify_lead","microsoft_dynamics_365_search_records","microsoft_dynamics_365_update_record","microsoft_excel_calculate","microsoft_excel_clear_range","microsoft_excel_close_session","microsoft_excel_create_session","microsoft_excel_create_table","microsoft_excel_delete_table","microsoft_excel_delete_table_row","microsoft_excel_delete_worksheet","microsoft_excel_format_range","microsoft_excel_get_table","microsoft_excel_get_table_row","microsoft_excel_get_worksheet","microsoft_excel_list_table_rows","microsoft_excel_list_tables","microsoft_excel_list_worksheets","microsoft_excel_read","microsoft_excel_read_v2","microsoft_excel_refresh_session","microsoft_excel_sort_range","microsoft_excel_table_add","microsoft_excel_update_table","microsoft_excel_update_table_row","microsoft_excel_update_worksheet","microsoft_excel_worksheet_add","microsoft_excel_write","microsoft_excel_write_v2","microsoft_planner_create_bucket","microsoft_planner_create_plan","microsoft_planner_create_task","microsoft_planner_delete_bucket","microsoft_planner_delete_plan","microsoft_planner_delete_task","microsoft_planner_get_plan_details","microsoft_planner_get_task_details","microsoft_planner_list_buckets","microsoft_planner_list_plans","microsoft_planner_read_bucket","microsoft_planner_read_plan","microsoft_planner_read_task","microsoft_planner_update_bucket","microsoft_planner_update_plan","microsoft_planner_update_plan_details","microsoft_planner_update_task","microsoft_planner_update_task_details","microsoft_teams_delete_channel_message","microsoft_teams_delete_chat_message","microsoft_teams_get_message","microsoft_teams_list_channel_members","microsoft_teams_list_channels","microsoft_teams_list_chat_members","microsoft_teams_list_chats","microsoft_teams_list_team_members","microsoft_teams_list_teams","microsoft_teams_read_channel","microsoft_teams_read_chat","microsoft_teams_reply_to_message","microsoft_teams_set_reaction","microsoft_teams_unset_reaction","microsoft_teams_update_channel_message","microsoft_teams_update_chat_message","microsoft_teams_write_channel","microsoft_teams_write_chat","microsoft_word_append","microsoft_word_create","microsoft_word_create_from_template","microsoft_word_export_pdf","microsoft_word_list","microsoft_word_read","microsoft_word_replace_text","microsoft_word_update","millionverifier_get_credits","millionverifier_verify_email","mintlify_create_agent_job","mintlify_create_assistant_message","mintlify_detect_ai_prose","mintlify_get_agent_job","mintlify_get_assistant_caller_stats","mintlify_get_assistant_conversations","mintlify_get_feedback","mintlify_get_feedback_by_page","mintlify_get_page_content","mintlify_get_searches","mintlify_get_update_status","mintlify_get_views","mintlify_get_visitors","mintlify_search","mintlify_send_agent_message","mintlify_trigger_automation","mintlify_trigger_preview","mintlify_trigger_update","mistral_parser","mistral_parser_v2","mistral_parser_v3","modal_call_function","modal_chat_completion","modal_list_models","monaco_add_audience_contacts","monaco_add_campaign_audiences","monaco_create_audience","monaco_create_contact","monaco_create_opportunity","monaco_create_tag","monaco_create_task","monaco_delete_account","monaco_delete_contact","monaco_delete_opportunity","monaco_enroll_campaign_contacts","monaco_get_account","monaco_get_audience","monaco_get_campaign","monaco_get_contact","monaco_get_conversation","monaco_get_current_user","monaco_get_field_schemas","monaco_get_meeting","monaco_get_opportunity","monaco_get_sequence","monaco_get_task","monaco_link_opportunity_contacts","monaco_list_accounts","monaco_list_audience_contacts","monaco_list_audiences","monaco_list_campaigns","monaco_list_contacts","monaco_list_meetings","monaco_list_messages","monaco_list_opportunities","monaco_list_opportunity_contacts","monaco_list_pipelines","monaco_list_sequences","monaco_list_tags","monaco_list_tasks","monaco_list_users","monaco_remove_audience_contacts","monaco_unlink_opportunity_contacts","monaco_update_account","monaco_update_contact","monaco_update_opportunity","monaco_update_sequence","monaco_update_task","monaco_upsert_account","monaco_upsert_contact","monday_archive_item","monday_change_column_value","monday_create_board","monday_create_column","monday_create_group","monday_create_item","monday_create_subitem","monday_create_update","monday_delete_item","monday_duplicate_item","monday_get_board","monday_get_groups","monday_get_item","monday_get_items","monday_list_boards","monday_move_item_to_group","monday_search_items","monday_update_item","mongodb_delete","mongodb_execute","mongodb_insert","mongodb_introspect","mongodb_query","mongodb_update","mssql_delete","mssql_execute","mssql_insert","mssql_introspect","mssql_query","mssql_update","mysql_delete","mysql_execute","mysql_insert","mysql_introspect","mysql_query","mysql_update","neo4j_create","neo4j_delete","neo4j_execute","neo4j_introspect","neo4j_merge","neo4j_query","neo4j_update","netsuite_attach_record","netsuite_batch_create_records","netsuite_batch_delete_records","netsuite_batch_get_records","netsuite_batch_update_records","netsuite_batch_upsert_records","netsuite_create_record","netsuite_delete_record","netsuite_detach_record","netsuite_execute_action","netsuite_execute_dataset","netsuite_execute_suiteql","netsuite_get_async_result","netsuite_get_async_status","netsuite_get_governance_limits","netsuite_get_record","netsuite_get_record_form","netsuite_get_record_metadata","netsuite_get_select_options","netsuite_get_server_time","netsuite_get_subresource","netsuite_list_datasets","netsuite_list_record_types","netsuite_list_records","netsuite_transform_record","netsuite_update_record","netsuite_upsert_record","neverbounce_get_credits","neverbounce_verify_email","new_relic_create_deployment_event","new_relic_get_entity","new_relic_nrql_query","new_relic_search_entities","notion_add_database_row","notion_add_database_row_v2","notion_append_blocks","notion_append_blocks_v2","notion_create_comment","notion_create_comment_v2","notion_create_database","notion_create_database_v2","notion_create_page","notion_create_page_v2","notion_delete_block","notion_delete_block_v2","notion_list_comments","notion_list_comments_v2","notion_list_users","notion_list_users_v2","notion_query_database","notion_query_database_v2","notion_read","notion_read_database","notion_read_database_v2","notion_read_v2","notion_retrieve_block","notion_retrieve_block_children","notion_retrieve_block_children_v2","notion_retrieve_block_v2","notion_retrieve_user","notion_retrieve_user_v2","notion_search","notion_search_v2","notion_update_block","notion_update_block_v2","notion_update_page","notion_update_page_v2","notion_write","notion_write_v2","obsidian_append_active","obsidian_append_note","obsidian_append_periodic_note","obsidian_create_note","obsidian_delete_note","obsidian_execute_command","obsidian_get_active","obsidian_get_note","obsidian_get_periodic_note","obsidian_list_commands","obsidian_list_files","obsidian_open_file","obsidian_patch_active","obsidian_patch_note","obsidian_search","okta_activate_group_rule","okta_activate_user","okta_add_user_to_group","okta_assign_group_to_app","okta_assign_user_role","okta_assign_user_to_app","okta_clear_user_sessions","okta_create_group","okta_create_group_rule","okta_create_user","okta_deactivate_group_rule","okta_deactivate_user","okta_delete_group","okta_delete_group_rule","okta_delete_user","okta_enroll_factor","okta_get_app","okta_get_factor","okta_get_group","okta_get_group_rule","okta_get_logs","okta_get_session","okta_get_user","okta_list_app_groups","okta_list_app_users","okta_list_apps","okta_list_factors","okta_list_group_members","okta_list_group_rules","okta_list_groups","okta_list_user_roles","okta_list_users","okta_remove_group_from_app","okta_remove_user_from_app","okta_remove_user_from_group","okta_remove_user_role","okta_reset_all_factors","okta_reset_factor","okta_reset_password","okta_revoke_session","okta_suspend_user","okta_unsuspend_user","okta_update_group","okta_update_user","onedrive_copy","onedrive_create_folder","onedrive_create_share_link","onedrive_delete","onedrive_download","onedrive_get_drive_info","onedrive_get_item","onedrive_list","onedrive_move","onedrive_search","onedrive_upload","onepassword_create_item","onepassword_delete_item","onepassword_get_item","onepassword_get_item_file","onepassword_get_vault","onepassword_list_items","onepassword_list_vaults","onepassword_replace_item","onepassword_resolve_secret","onepassword_update_item","openai_embeddings","openai_image","oracle_fusion_financials_apply_receivables_receipt","oracle_fusion_financials_approve_receivables_invoice","oracle_fusion_financials_create_expense_line","oracle_fusion_financials_create_expense_report","oracle_fusion_financials_create_receivables_credit_memo","oracle_fusion_financials_create_receivables_invoice","oracle_fusion_financials_create_receivables_receipt","oracle_fusion_financials_get_expense_line","oracle_fusion_financials_get_expense_report","oracle_fusion_financials_get_gl_journal_batch","oracle_fusion_financials_get_gl_ledger","oracle_fusion_financials_get_payables_invoice","oracle_fusion_financials_get_payables_invoice_line","oracle_fusion_financials_get_payables_payment","oracle_fusion_financials_get_receivables_credit_memo","oracle_fusion_financials_get_receivables_invoice","oracle_fusion_financials_get_receivables_invoice_installment","oracle_fusion_financials_get_receivables_invoice_line","oracle_fusion_financials_get_receivables_receipt","oracle_fusion_financials_list_expense_lines","oracle_fusion_financials_list_expense_reports","oracle_fusion_financials_list_gl_balances","oracle_fusion_financials_list_gl_journal_batches","oracle_fusion_financials_list_gl_ledgers","oracle_fusion_financials_list_payables_invoice_lines","oracle_fusion_financials_list_payables_invoices","oracle_fusion_financials_list_payables_payments","oracle_fusion_financials_list_receivables_credit_memos","oracle_fusion_financials_list_receivables_invoice_installments","oracle_fusion_financials_list_receivables_invoice_lines","oracle_fusion_financials_list_receivables_invoices","oracle_fusion_financials_list_receivables_receipts","oracle_fusion_financials_submit_expense_report","oracle_fusion_financials_update_expense_line","oracle_fusion_financials_update_expense_report","oracle_fusion_financials_update_receivables_invoice","oracledb_delete","oracledb_execute","oracledb_insert","oracledb_introspect","oracledb_query","oracledb_update","otter_create_conversation","otter_get_conversation","otter_get_conversation_audio","otter_get_workspace","otter_list_channel_members","otter_list_channels","otter_list_conversations","otter_list_workspace_conversations","outlook_calendar_create_event","outlook_calendar_delete_event","outlook_calendar_get_event","outlook_calendar_list_events","outlook_calendar_respond","outlook_calendar_update_event","outlook_copy","outlook_create_folder","outlook_delete","outlook_draft","outlook_forward","outlook_get_attachment","outlook_list_attachments","outlook_list_folders","outlook_mark_read","outlook_mark_unread","outlook_move","outlook_read","outlook_reply","outlook_reply_all","outlook_search","outlook_send","outlook_update_message","pagerduty_add_note","pagerduty_create_incident","pagerduty_get_incident","pagerduty_get_service","pagerduty_list_escalation_policies","pagerduty_list_incident_alerts","pagerduty_list_incidents","pagerduty_list_oncalls","pagerduty_list_schedules","pagerduty_list_services","pagerduty_list_users","pagerduty_merge_incidents","pagerduty_send_event","pagerduty_snooze_incident","pagerduty_update_incident","parallel_deep_research","parallel_extract","parallel_search","pdl_autocomplete","pdl_bulk_company_enrich","pdl_bulk_person_enrich","pdl_clean_company","pdl_clean_location","pdl_clean_school","pdl_company_enrich","pdl_company_search","pdl_person_enrich","pdl_person_identify","pdl_person_search","perplexity_chat","perplexity_search","persona_approve_inquiry","persona_create_account","persona_create_inquiry","persona_create_report","persona_decline_inquiry","persona_expire_inquiry","persona_generate_inquiry_link","persona_get_account","persona_get_case","persona_get_document","persona_get_inquiry","persona_get_report","persona_get_verification","persona_import_accounts","persona_list_accounts","persona_list_cases","persona_list_inquiries","persona_list_inquiry_templates","persona_list_reports","persona_mark_inquiry_for_review","persona_print_inquiry_pdf","persona_redact_account","persona_redact_inquiry","persona_resume_inquiry","persona_update_account","persona_update_inquiry","pinecone_delete_vectors","pinecone_describe_index","pinecone_describe_index_stats","pinecone_fetch","pinecone_generate_embeddings","pinecone_list_indexes","pinecone_list_vector_ids","pinecone_search_text","pinecone_search_vector","pinecone_update_vector","pinecone_upsert_text","pipedrive_create_activity","pipedrive_create_deal","pipedrive_create_lead","pipedrive_create_project","pipedrive_delete_lead","pipedrive_get_activities","pipedrive_get_all_deals","pipedrive_get_deal","pipedrive_get_files","pipedrive_get_leads","pipedrive_get_mail_messages","pipedrive_get_mail_thread","pipedrive_get_pipeline_deals","pipedrive_get_pipelines","pipedrive_get_projects","pipedrive_update_activity","pipedrive_update_deal","pipedrive_update_lead","pitchbook_company_active_investors","pitchbook_company_bio","pitchbook_company_deal_service_providers","pitchbook_company_deals","pitchbook_company_financials","pitchbook_company_general_service_providers","pitchbook_company_industries","pitchbook_company_investors","pitchbook_company_most_recent_debt_financing","pitchbook_company_most_recent_financials","pitchbook_company_most_recent_financing","pitchbook_company_search","pitchbook_company_similar_companies","pitchbook_company_social_analytics","pitchbook_company_updates","pitchbook_company_vc_exit_predictions","pitchbook_contracts_history","pitchbook_cost_of_calls","pitchbook_credit_history","pitchbook_credit_news","pitchbook_credit_news_bulk","pitchbook_credit_news_most_recent","pitchbook_credit_news_search","pitchbook_deal_bio","pitchbook_deal_cap_table_history","pitchbook_deal_debt_lenders","pitchbook_deal_detailed","pitchbook_deal_investors","pitchbook_deal_multiples","pitchbook_deal_search","pitchbook_deal_service_providers","pitchbook_deal_stock_info","pitchbook_deal_tranche_info","pitchbook_deal_updates","pitchbook_deal_valuation","pitchbook_entity_affiliates","pitchbook_entity_locations","pitchbook_entity_news","pitchbook_entity_people","pitchbook_entity_updates","pitchbook_fund_active_investments","pitchbook_fund_benchmark","pitchbook_fund_bio","pitchbook_fund_cash_flows","pitchbook_fund_commitments","pitchbook_fund_investment_preferences","pitchbook_fund_investments","pitchbook_fund_performance","pitchbook_fund_search","pitchbook_fund_team","pitchbook_fund_updates","pitchbook_investor_active_investments","pitchbook_investor_bio","pitchbook_investor_board_seats","pitchbook_investor_deal_service_providers","pitchbook_investor_funds","pitchbook_investor_general_service_providers","pitchbook_investor_investments","pitchbook_investor_last_closed_fund","pitchbook_investor_preferences","pitchbook_investor_search","pitchbook_investor_updates","pitchbook_limited_partner_actual_allocations","pitchbook_limited_partner_bio","pitchbook_limited_partner_commitment_aggregates","pitchbook_limited_partner_commitment_preferences","pitchbook_limited_partner_commitments_detailed","pitchbook_limited_partner_search","pitchbook_limited_partner_service_providers","pitchbook_limited_partner_target_allocations","pitchbook_limited_partner_updates","pitchbook_lookup_table_structure","pitchbook_lookup_tables","pitchbook_patent_detailed","pitchbook_patent_search","pitchbook_people_search","pitchbook_person_bio","pitchbook_person_contact","pitchbook_person_education_work","pitchbook_sandbox_entities","pitchbook_search","pitchbook_service_provider_bio","pitchbook_service_provider_search","pitchbook_service_provider_updates","pitchbook_serviced_companies","pitchbook_serviced_deals","pitchbook_serviced_funds","pitchbook_serviced_investors","pitchbook_serviced_limited_partners","pitchbook_shared_search","pitchbook_usage_report","plane_archive_project","plane_archive_work_item","plane_confirm_attachment_upload","plane_create_attachment_upload","plane_create_comment","plane_create_cycle","plane_create_label","plane_create_link","plane_create_module","plane_create_project","plane_create_project_page","plane_create_state","plane_create_webhook","plane_create_work_item","plane_delete_attachment","plane_delete_comment","plane_delete_cycle","plane_delete_label","plane_delete_link","plane_delete_module","plane_delete_project","plane_delete_project_page","plane_delete_state","plane_delete_webhook","plane_delete_work_item","plane_get_attachment","plane_get_comment","plane_get_current_user","plane_get_cycle","plane_get_label","plane_get_link","plane_get_module","plane_get_project","plane_get_project_page","plane_get_project_summary","plane_get_state","plane_get_webhook","plane_get_work_item","plane_get_work_item_by_identifier","plane_list_attachments","plane_list_comments","plane_list_cycles","plane_list_labels","plane_list_links","plane_list_modules","plane_list_project_pages","plane_list_projects","plane_list_states","plane_list_webhooks","plane_list_work_items","plane_list_workspace_work_items","plane_manage_cycle_work_items","plane_manage_module_work_items","plane_regenerate_webhook_secret","plane_transfer_cycle_work_items","plane_unarchive_project","plane_unarchive_work_item","plane_update_comment","plane_update_cycle","plane_update_label","plane_update_link","plane_update_module","plane_update_project","plane_update_project_page","plane_update_state","plane_update_webhook","plane_update_work_item","planetscale_close_deploy_request","planetscale_create_backup","planetscale_create_branch","planetscale_create_deploy_request","planetscale_delete_branch","planetscale_get_backup","planetscale_get_branch","planetscale_get_database","planetscale_get_deploy_request","planetscale_list_backups","planetscale_list_branches","planetscale_list_databases","planetscale_list_deploy_requests","planetscale_queue_deploy_request","planetscale_review_deploy_request","polymarket_get_activity","polymarket_get_event","polymarket_get_events","polymarket_get_holders","polymarket_get_last_trade_price","polymarket_get_leaderboard","polymarket_get_market","polymarket_get_markets","polymarket_get_midpoint","polymarket_get_orderbook","polymarket_get_positions","polymarket_get_price","polymarket_get_price_history","polymarket_get_series","polymarket_get_series_by_id","polymarket_get_spread","polymarket_get_tags","polymarket_get_tick_size","polymarket_get_trades","polymarket_search","postgresql_delete","postgresql_execute","postgresql_insert","postgresql_introspect","postgresql_query","postgresql_update","posthog_batch_events","posthog_capture_event","posthog_create_annotation","posthog_create_cohort","posthog_create_dashboard","posthog_create_experiment","posthog_create_feature_flag","posthog_create_insight","posthog_create_survey","posthog_delete_feature_flag","posthog_delete_person","posthog_delete_survey","posthog_evaluate_flags","posthog_get_cohort","posthog_get_dashboard","posthog_get_event_definition","posthog_get_experiment","posthog_get_feature_flag","posthog_get_insight","posthog_get_organization","posthog_get_person","posthog_get_project","posthog_get_property_definition","posthog_get_session_recording","posthog_get_survey","posthog_list_actions","posthog_list_annotations","posthog_list_cohorts","posthog_list_dashboards","posthog_list_event_definitions","posthog_list_experiments","posthog_list_feature_flags","posthog_list_insights","posthog_list_organizations","posthog_list_persons","posthog_list_projects","posthog_list_property_definitions","posthog_list_recording_playlists","posthog_list_session_recordings","posthog_list_surveys","posthog_query","posthog_update_cohort","posthog_update_event_definition","posthog_update_experiment","posthog_update_feature_flag","posthog_update_insight","posthog_update_property_definition","posthog_update_survey","powerbi_execute_query","powerbi_get_dataset","powerbi_get_refresh_history","powerbi_get_report","powerbi_list_datasets","powerbi_list_reports","powerbi_list_workspaces","powerbi_refresh_dataset","profound_bot_logs","profound_bots_report","profound_category_assets","profound_category_personas","profound_category_prompts","profound_category_tags","profound_category_topics","profound_citation_prompts","profound_citations_report","profound_list_assets","profound_list_categories","profound_list_domains","profound_list_models","profound_list_optimizations","profound_list_personas","profound_list_regions","profound_optimization_analysis","profound_prompt_answers","profound_prompt_volume","profound_query_fanouts","profound_raw_logs","profound_referrals_report","profound_sentiment_report","profound_visibility_report","prospeo_account_information","prospeo_bulk_enrich_company","prospeo_bulk_enrich_person","prospeo_enrich_company","prospeo_enrich_person","prospeo_search_company","prospeo_search_person","prospeo_search_suggestions","pulse_parser","pulse_parser_v2","qdrant_fetch_points","qdrant_search_vector","qdrant_upsert_points","quartr_get_audio","quartr_get_company","quartr_get_event","quartr_get_event_summary","quartr_get_report","quartr_get_slide_deck","quartr_get_transcript","quartr_list_audio","quartr_list_companies","quartr_list_document_types","quartr_list_documents","quartr_list_event_types","quartr_list_events","quartr_list_live_events","quartr_list_reports","quartr_list_slide_decks","quartr_list_transcripts","quickbooks_add_attachment","quickbooks_create_bill","quickbooks_create_bill_payment","quickbooks_create_credit_memo","quickbooks_create_customer","quickbooks_create_customer_payment","quickbooks_create_deposit","quickbooks_create_employee","quickbooks_create_estimate","quickbooks_create_invoice","quickbooks_create_item","quickbooks_create_journal_entry","quickbooks_create_purchase","quickbooks_create_purchase_order","quickbooks_create_refund_receipt","quickbooks_create_sales_receipt","quickbooks_create_vendor","quickbooks_create_vendor_credit","quickbooks_download_attachment","quickbooks_download_transaction_pdf","quickbooks_email_transaction","quickbooks_get_company_info","quickbooks_read_accounting_transactions","quickbooks_read_attachments","quickbooks_read_master_data","quickbooks_read_purchasing_transactions","quickbooks_read_sales_transactions","quickbooks_run_financial_report","quickbooks_update_bill","quickbooks_update_bill_payment","quickbooks_update_credit_memo","quickbooks_update_customer","quickbooks_update_customer_payment","quickbooks_update_deposit","quickbooks_update_employee","quickbooks_update_estimate","quickbooks_update_invoice","quickbooks_update_item","quickbooks_update_journal_entry","quickbooks_update_purchase","quickbooks_update_purchase_order","quickbooks_update_refund_receipt","quickbooks_update_sales_receipt","quickbooks_update_vendor","quickbooks_update_vendor_credit","quickbooks_void_bill_payment","quickbooks_void_customer_payment","quickbooks_void_invoice","quickbooks_void_sales_receipt","quiver_image_to_svg","quiver_image_to_svg_v2","quiver_list_models","quiver_text_to_svg","quiver_text_to_svg_v2","rabbitmq_create_binding","rabbitmq_create_exchange","rabbitmq_create_policy","rabbitmq_create_queue","rabbitmq_delete_binding","rabbitmq_delete_exchange","rabbitmq_delete_policy","rabbitmq_delete_queue","rabbitmq_get_exchange","rabbitmq_get_messages","rabbitmq_get_overview","rabbitmq_get_queue","rabbitmq_health_check","rabbitmq_list_bindings","rabbitmq_list_channels","rabbitmq_list_connections","rabbitmq_list_consumers","rabbitmq_list_exchange_bindings","rabbitmq_list_exchanges","rabbitmq_list_nodes","rabbitmq_list_policies","rabbitmq_list_queues","rabbitmq_list_vhosts","rabbitmq_publish_message","rabbitmq_purge_queue","railway_create_environment","railway_create_project","railway_create_service","railway_delete_environment","railway_delete_project","railway_delete_service","railway_delete_variable","railway_deploy_service","railway_get_deployment","railway_get_deployment_logs","railway_get_project","railway_list_deployments","railway_list_project_members","railway_list_projects","railway_list_variables","railway_restart_deployment","railway_rollback_deployment","railway_transfer_project","railway_update_project","railway_upsert_variable","ramp_create_memo","ramp_get_balance","ramp_get_bill","ramp_get_business","ramp_get_memo","ramp_get_physical_card","ramp_get_reimbursement","ramp_get_transaction","ramp_get_user","ramp_get_vendor","ramp_get_virtual_card","ramp_list_bills","ramp_list_physical_cards","ramp_list_reimbursements","ramp_list_transactions","ramp_list_users","ramp_list_vendors","ramp_list_virtual_cards","rb2b_credit_check","rb2b_email_to_activity","rb2b_hem_to_best_linkedin","rb2b_hem_to_business_profile","rb2b_hem_to_linkedin","rb2b_hem_to_maid","rb2b_ip_to_company","rb2b_ip_to_hem","rb2b_ip_to_maid","rb2b_linkedin_slug_search","rb2b_linkedin_to_best_personal_email","rb2b_linkedin_to_business_profile","rb2b_linkedin_to_hashed_emails","rb2b_linkedin_to_mobile_phone","rb2b_linkedin_to_personal_email","rds_delete","rds_execute","rds_insert","rds_introspect","rds_query","rds_update","reddit_delete","reddit_edit","reddit_get_comments","reddit_get_controversial","reddit_get_info","reddit_get_me","reddit_get_messages","reddit_get_posts","reddit_get_saved","reddit_get_subreddit_info","reddit_get_subreddit_rules","reddit_get_user","reddit_get_user_comments","reddit_get_user_posts","reddit_hide","reddit_hot_posts","reddit_list_my_subreddits","reddit_lock","reddit_mark_all_read","reddit_mark_read","reddit_marknsfw","reddit_mod_approve","reddit_mod_distinguish","reddit_mod_remove","reddit_mod_sticky","reddit_reply","reddit_report","reddit_save","reddit_search","reddit_search_subreddits","reddit_send_message","reddit_submit_post","reddit_subscribe","reddit_unhide","reddit_unlock","reddit_unmarknsfw","reddit_unsave","reddit_vote","redis_command","redis_delete","redis_exists","redis_expire","redis_get","redis_hdel","redis_hget","redis_hgetall","redis_hset","redis_incr","redis_incrby","redis_keys","redis_llen","redis_lpop","redis_lpush","redis_lrange","redis_persist","redis_rpop","redis_rpush","redis_set","redis_setnx","redis_ttl","reducto_parser","reducto_parser_v2","resend_cancel_email","resend_create_audience","resend_create_broadcast","resend_create_contact","resend_delete_audience","resend_delete_contact","resend_get_audience","resend_get_broadcast","resend_get_contact","resend_get_email","resend_list_audiences","resend_list_contacts","resend_list_domains","resend_send","resend_send_broadcast","resend_update_contact","revenuecat_create_purchase","revenuecat_defer_google_subscription","revenuecat_delete_customer","revenuecat_get_customer","revenuecat_grant_entitlement","revenuecat_list_offerings","revenuecat_refund_google_subscription","revenuecat_revoke_entitlement","revenuecat_revoke_google_subscription","revenuecat_update_subscriber_attributes","rippling_bulk_create_custom_object_records","rippling_bulk_delete_custom_object_records","rippling_bulk_update_custom_object_records","rippling_create_business_partner","rippling_create_business_partner_group","rippling_create_custom_app","rippling_create_custom_object","rippling_create_custom_object_field","rippling_create_custom_object_record","rippling_create_custom_page","rippling_create_custom_setting","rippling_create_department","rippling_create_draft_hires","rippling_create_object_category","rippling_create_title","rippling_create_work_location","rippling_delete_business_partner","rippling_delete_business_partner_group","rippling_delete_custom_app","rippling_delete_custom_object","rippling_delete_custom_object_field","rippling_delete_custom_object_record","rippling_delete_custom_page","rippling_delete_custom_setting","rippling_delete_object_category","rippling_delete_title","rippling_delete_work_location","rippling_get_business_partner","rippling_get_business_partner_group","rippling_get_current_user","rippling_get_custom_app","rippling_get_custom_object","rippling_get_custom_object_field","rippling_get_custom_object_record","rippling_get_custom_object_record_by_external_id","rippling_get_custom_page","rippling_get_custom_setting","rippling_get_department","rippling_get_employment_type","rippling_get_job_function","rippling_get_object_category","rippling_get_report_run","rippling_get_supergroup","rippling_get_team","rippling_get_title","rippling_get_user","rippling_get_work_location","rippling_get_worker","rippling_list_business_partner_groups","rippling_list_business_partners","rippling_list_companies","rippling_list_custom_apps","rippling_list_custom_fields","rippling_list_custom_object_fields","rippling_list_custom_object_records","rippling_list_custom_objects","rippling_list_custom_pages","rippling_list_custom_settings","rippling_list_departments","rippling_list_employment_types","rippling_list_entitlements","rippling_list_job_functions","rippling_list_object_categories","rippling_list_supergroup_exclusion_members","rippling_list_supergroup_inclusion_members","rippling_list_supergroup_members","rippling_list_supergroups","rippling_list_teams","rippling_list_titles","rippling_list_users","rippling_list_work_locations","rippling_list_workers","rippling_query_custom_object_records","rippling_trigger_report_run","rippling_update_custom_app","rippling_update_custom_object","rippling_update_custom_object_field","rippling_update_custom_object_record","rippling_update_custom_page","rippling_update_custom_setting","rippling_update_department","rippling_update_object_category","rippling_update_supergroup_exclusion_members","rippling_update_supergroup_inclusion_members","rippling_update_title","rippling_update_work_location","rocketlane_add_field_option","rocketlane_add_project_members","rocketlane_add_task_assignees","rocketlane_add_task_dependencies","rocketlane_add_task_followers","rocketlane_archive_project","rocketlane_assign_placeholders","rocketlane_create_field","rocketlane_create_phase","rocketlane_create_project","rocketlane_create_space","rocketlane_create_space_document","rocketlane_create_task","rocketlane_create_time_entry","rocketlane_create_time_off","rocketlane_delete_field","rocketlane_delete_phase","rocketlane_delete_project","rocketlane_delete_space","rocketlane_delete_space_document","rocketlane_delete_task","rocketlane_delete_time_entry","rocketlane_delete_time_off","rocketlane_get_field","rocketlane_get_invoice","rocketlane_get_invoice_line_items","rocketlane_get_invoice_payments","rocketlane_get_phase","rocketlane_get_project","rocketlane_get_space","rocketlane_get_space_document","rocketlane_get_task","rocketlane_get_time_entry","rocketlane_get_time_off","rocketlane_get_user","rocketlane_import_template","rocketlane_list_fields","rocketlane_list_invoices","rocketlane_list_phases","rocketlane_list_placeholders","rocketlane_list_projects","rocketlane_list_resource_allocations","rocketlane_list_space_documents","rocketlane_list_spaces","rocketlane_list_tasks","rocketlane_list_time_entries","rocketlane_list_time_entry_categories","rocketlane_list_time_offs","rocketlane_list_users","rocketlane_move_task_to_phase","rocketlane_remove_project_members","rocketlane_remove_task_assignees","rocketlane_remove_task_dependencies","rocketlane_remove_task_followers","rocketlane_search_time_entries","rocketlane_unassign_placeholders","rocketlane_update_field","rocketlane_update_field_option","rocketlane_update_phase","rocketlane_update_project","rocketlane_update_space","rocketlane_update_space_document","rocketlane_update_task","rocketlane_update_time_entry","rootly_acknowledge_alert","rootly_add_incident_event","rootly_add_subscribers","rootly_assign_incident_role","rootly_create_action_item","rootly_create_alert","rootly_create_incident","rootly_create_status_page_event","rootly_delete_action_item","rootly_delete_incident","rootly_escalate_alert","rootly_get_alert","rootly_get_incident","rootly_list_action_items","rootly_list_alerts","rootly_list_causes","rootly_list_environments","rootly_list_escalation_policies","rootly_list_functionalities","rootly_list_incident_events","rootly_list_incident_roles","rootly_list_incident_types","rootly_list_incidents","rootly_list_on_calls","rootly_list_playbooks","rootly_list_retrospectives","rootly_list_schedules","rootly_list_services","rootly_list_severities","rootly_list_teams","rootly_list_users","rootly_mitigate_incident","rootly_remove_subscribers","rootly_resolve_alert","rootly_resolve_incident","rootly_run_workflow","rootly_snooze_alert","rootly_unassign_incident_role","rootly_update_action_item","rootly_update_alert","rootly_update_incident","s3_copy_object","s3_create_bucket","s3_delete_bucket","s3_delete_object","s3_delete_objects","s3_get_object","s3_head_object","s3_list_buckets","s3_list_objects","s3_presigned_url","s3_put_object","sailpoint_approve_access_request","sailpoint_cancel_access_request","sailpoint_decide_certification_review_items","sailpoint_get_access_profile","sailpoint_get_access_profile_entitlements","sailpoint_get_access_request_config","sailpoint_get_access_request_status","sailpoint_get_account","sailpoint_get_account_activity","sailpoint_get_account_entitlements","sailpoint_get_account_selections","sailpoint_get_campaign","sailpoint_get_certification","sailpoint_get_entitlement","sailpoint_get_entitlement_request_config","sailpoint_get_identity","sailpoint_get_role","sailpoint_get_role_entitlements","sailpoint_get_source","sailpoint_get_task_status","sailpoint_list_access_profiles","sailpoint_list_account_activities","sailpoint_list_accounts","sailpoint_list_campaigns","sailpoint_list_certification_review_items","sailpoint_list_certifications","sailpoint_list_entitlements","sailpoint_list_identities","sailpoint_list_identity_entitlements","sailpoint_list_pending_access_request_approvals","sailpoint_list_roles","sailpoint_list_sources","sailpoint_load_accounts","sailpoint_load_entitlements","sailpoint_reject_access_request","sailpoint_request_access","sailpoint_search","sailpoint_search_aggregate","sailpoint_search_count","sailpoint_sign_off_certification","salesforce_create_account","salesforce_create_case","salesforce_create_contact","salesforce_create_custom_field","salesforce_create_custom_object","salesforce_create_lead","salesforce_create_opportunity","salesforce_create_task","salesforce_delete_account","salesforce_delete_case","salesforce_delete_contact","salesforce_delete_custom_field","salesforce_delete_lead","salesforce_delete_opportunity","salesforce_delete_task","salesforce_describe_object","salesforce_get_accounts","salesforce_get_cases","salesforce_get_contacts","salesforce_get_dashboard","salesforce_get_leads","salesforce_get_opportunities","salesforce_get_report","salesforce_get_tasks","salesforce_list_dashboards","salesforce_list_objects","salesforce_list_report_types","salesforce_list_reports","salesforce_query","salesforce_query_more","salesforce_refresh_dashboard","salesforce_run_report","salesforce_tooling_query","salesforce_update_account","salesforce_update_case","salesforce_update_contact","salesforce_update_custom_field","salesforce_update_lead","salesforce_update_opportunity","salesforce_update_task","sap_concur_approve_expense_report","sap_concur_associate_attendees","sap_concur_create_cash_advance","sap_concur_create_expected_expense","sap_concur_create_expense_report","sap_concur_create_list_item","sap_concur_create_purchase_request","sap_concur_create_quick_expense","sap_concur_create_quick_expense_with_image","sap_concur_create_report_comment","sap_concur_create_travel_request","sap_concur_create_user","sap_concur_delete_expected_expense","sap_concur_delete_expense","sap_concur_delete_expense_report","sap_concur_delete_list_item","sap_concur_delete_travel_request","sap_concur_delete_user","sap_concur_get_allocation","sap_concur_get_budget","sap_concur_get_cash_advance","sap_concur_get_expected_expense","sap_concur_get_expense","sap_concur_get_expense_report","sap_concur_get_itemizations","sap_concur_get_itinerary","sap_concur_get_list","sap_concur_get_list_item","sap_concur_get_purchase_request","sap_concur_get_receipt","sap_concur_get_receipt_status","sap_concur_get_request_cash_advance","sap_concur_get_travel_profile","sap_concur_get_travel_request","sap_concur_get_user","sap_concur_issue_cash_advance","sap_concur_list_allocations","sap_concur_list_attendee_associations","sap_concur_list_budget_categories","sap_concur_list_budgets","sap_concur_list_exceptions","sap_concur_list_expected_expenses","sap_concur_list_expense_reports","sap_concur_list_expenses","sap_concur_list_itineraries","sap_concur_list_list_items","sap_concur_list_lists","sap_concur_list_receipts","sap_concur_list_report_comments","sap_concur_list_reports_to_approve","sap_concur_list_travel_profiles_summary","sap_concur_list_travel_request_comments","sap_concur_list_travel_requests","sap_concur_list_users","sap_concur_move_travel_request","sap_concur_recall_expense_report","sap_concur_remove_all_attendees","sap_concur_search_locations","sap_concur_search_users","sap_concur_send_back_expense_report","sap_concur_submit_expense_report","sap_concur_update_allocation","sap_concur_update_expected_expense","sap_concur_update_expense","sap_concur_update_expense_report","sap_concur_update_list_item","sap_concur_update_travel_request","sap_concur_update_user","sap_concur_upload_exchange_rates","sap_concur_upload_receipt_image","sap_s4hana_create_business_partner","sap_s4hana_create_purchase_order","sap_s4hana_create_purchase_requisition","sap_s4hana_create_sales_order","sap_s4hana_delete_sales_order","sap_s4hana_get_billing_document","sap_s4hana_get_business_partner","sap_s4hana_get_customer","sap_s4hana_get_inbound_delivery","sap_s4hana_get_material_document","sap_s4hana_get_outbound_delivery","sap_s4hana_get_product","sap_s4hana_get_purchase_order","sap_s4hana_get_purchase_requisition","sap_s4hana_get_sales_order","sap_s4hana_get_supplier","sap_s4hana_get_supplier_invoice","sap_s4hana_list_billing_documents","sap_s4hana_list_business_partners","sap_s4hana_list_customers","sap_s4hana_list_inbound_deliveries","sap_s4hana_list_material_documents","sap_s4hana_list_material_stock","sap_s4hana_list_outbound_deliveries","sap_s4hana_list_products","sap_s4hana_list_purchase_orders","sap_s4hana_list_purchase_requisitions","sap_s4hana_list_sales_orders","sap_s4hana_list_supplier_invoices","sap_s4hana_list_suppliers","sap_s4hana_odata_query","sap_s4hana_update_business_partner","sap_s4hana_update_customer","sap_s4hana_update_product","sap_s4hana_update_purchase_order","sap_s4hana_update_purchase_requisition","sap_s4hana_update_sales_order","sap_s4hana_update_supplier","search_tool","secrets_manager_create_secret","secrets_manager_delete_secret","secrets_manager_describe_secret","secrets_manager_get_secret","secrets_manager_list_secrets","secrets_manager_restore_secret","secrets_manager_rotate_secret","secrets_manager_tag_resource","secrets_manager_untag_resource","secrets_manager_update_secret","semrush_backlinks","semrush_backlinks_anchors","semrush_backlinks_competitors","semrush_backlinks_geo_distribution","semrush_backlinks_indexed_pages","semrush_backlinks_overview","semrush_backlinks_tld_distribution","semrush_batch_keyword_overview","semrush_broad_match_keywords","semrush_domain_ad_copies","semrush_domain_ad_history","semrush_domain_organic_competitors","semrush_domain_organic_keywords","semrush_domain_overview","semrush_domain_overview_all","semrush_domain_overview_history","semrush_domain_paid_competitors","semrush_domain_paid_keywords","semrush_domain_pla_copies","semrush_domain_pla_keywords","semrush_domain_vs_domain","semrush_keyword_ad_history","semrush_keyword_difficulty","semrush_keyword_overview","semrush_keyword_overview_all","semrush_keyword_questions","semrush_organic_results","semrush_paid_results","semrush_referring_domains","semrush_referring_ips","semrush_related_keywords","semrush_subdomain_ad_copies","semrush_subdomain_organic_keywords","semrush_subdomain_overview","semrush_subdomain_overview_all","semrush_subdomain_overview_history","semrush_subdomain_paid_keywords","semrush_top_domains","semrush_url_organic_keywords","semrush_url_overview","semrush_url_overview_all","semrush_url_overview_history","semrush_url_paid_keywords","semrush_winners_and_losers","sendblue_evaluate_service","sendblue_get_message","sendblue_send_group_message","sendblue_send_message","sendblue_send_typing_indicator","sendgrid_add_contact","sendgrid_add_contacts_to_list","sendgrid_create_list","sendgrid_create_template","sendgrid_create_template_version","sendgrid_delete_contacts","sendgrid_delete_list","sendgrid_delete_template","sendgrid_get_contact","sendgrid_get_list","sendgrid_get_template","sendgrid_list_all_lists","sendgrid_list_templates","sendgrid_remove_contacts_from_list","sendgrid_search_contacts","sendgrid_send_mail","sentry_events_get","sentry_events_list","sentry_issues_get","sentry_issues_list","sentry_issues_update","sentry_projects_create","sentry_projects_get","sentry_projects_list","sentry_projects_update","sentry_releases_create","sentry_releases_deploy","sentry_releases_list","sentry_teams_list","serper_search","servicenow_add_incident_comment","servicenow_aggregate","servicenow_close_incident","servicenow_create_change_request","servicenow_create_incident","servicenow_create_record","servicenow_delete_record","servicenow_download_attachment","servicenow_download_attachment_v2","servicenow_find_user","servicenow_get_change_next_states","servicenow_get_change_request","servicenow_get_ci","servicenow_get_incident","servicenow_get_knowledge_article","servicenow_get_requested_item","servicenow_list_approvals","servicenow_list_attachments","servicenow_list_catalog_items","servicenow_list_change_requests","servicenow_list_change_tasks","servicenow_list_ci_relationships","servicenow_list_group_members","servicenow_list_incidents","servicenow_list_requested_items","servicenow_order_catalog_item","servicenow_read_record","servicenow_resolve_incident","servicenow_search_cis","servicenow_search_knowledge","servicenow_update_approval","servicenow_update_change_request","servicenow_update_change_state","servicenow_update_incident","servicenow_update_record","servicenow_upload_attachment","ses_create_configuration_set","ses_create_email_identity","ses_create_template","ses_delete_email_identity","ses_delete_suppressed_destination","ses_delete_template","ses_get_account","ses_get_email_identity","ses_get_suppressed_destination","ses_get_template","ses_list_identities","ses_list_suppressed_destinations","ses_list_templates","ses_put_suppressed_destination","ses_send_bulk_email","ses_send_custom_verification_email","ses_send_email","ses_send_templated_email","ses_update_template","sftp_delete","sftp_download","sftp_download_v2","sftp_list","sftp_mkdir","sftp_upload","sharepoint_add_list_items","sharepoint_create_list","sharepoint_create_page","sharepoint_delete_file","sharepoint_delete_list_item","sharepoint_delete_page","sharepoint_download_file","sharepoint_get_drive_item","sharepoint_get_list","sharepoint_get_list_item","sharepoint_list_sites","sharepoint_publish_page","sharepoint_read_page","sharepoint_update_list","sharepoint_update_page","sharepoint_upload_file","shopify_adjust_inventory","shopify_cancel_order","shopify_create_customer","shopify_create_fulfillment","shopify_create_product","shopify_create_product_variants","shopify_delete_customer","shopify_delete_product","shopify_get_collection","shopify_get_customer","shopify_get_fulfillment","shopify_get_fulfillment_order","shopify_get_inventory_level","shopify_get_job","shopify_get_order","shopify_get_product","shopify_list_collections","shopify_list_customers","shopify_list_fulfillment_orders","shopify_list_inventory_items","shopify_list_locations","shopify_list_orders","shopify_list_products","shopify_update_customer","shopify_update_fulfillment_tracking","shopify_update_order","shopify_update_product","shopify_update_product_variants","similarweb_bounce_rate","similarweb_page_views","similarweb_pages_per_visit","similarweb_traffic_visits","similarweb_visit_duration","similarweb_website_overview","sixtyfour_enrich_company","sixtyfour_enrich_lead","sixtyfour_find_email","sixtyfour_find_phone","slack_add_bookmark","slack_add_reaction","slack_archive_conversation","slack_canvas","slack_close_conversation","slack_create_channel_canvas","slack_create_conversation","slack_create_user_group","slack_delete_canvas","slack_delete_file","slack_delete_message","slack_delete_scheduled_message","slack_disable_user_group","slack_download","slack_edit_bookmark","slack_edit_canvas","slack_enable_user_group","slack_ephemeral_message","slack_get_canvas","slack_get_channel_history","slack_get_channel_info","slack_get_dnd_info","slack_get_file_info","slack_get_message","slack_get_permalink","slack_get_reactions","slack_get_team_dnd_info","slack_get_team_info","slack_get_team_profile","slack_get_thread","slack_get_thread_replies","slack_get_user","slack_get_user_presence","slack_get_user_profile","slack_invite_to_conversation","slack_join_conversation","slack_kick_conversation","slack_leave_conversation","slack_list_bookmarks","slack_list_canvases","slack_list_channels","slack_list_emoji","slack_list_files","slack_list_members","slack_list_pins","slack_list_reactions","slack_list_scheduled_messages","slack_list_user_conversations","slack_list_user_group_members","slack_list_user_groups","slack_list_users","slack_lists_access_delete","slack_lists_access_set","slack_lists_create","slack_lists_download_get","slack_lists_download_start","slack_lists_items_create","slack_lists_items_delete","slack_lists_items_delete_multiple","slack_lists_items_info","slack_lists_items_list","slack_lists_items_update","slack_lists_update","slack_lookup_canvas_sections","slack_lookup_user_by_email","slack_mark_conversation_read","slack_message","slack_message_reader","slack_open_conversation","slack_open_view","slack_pin_message","slack_publish_view","slack_push_view","slack_remove_bookmark","slack_remove_reaction","slack_rename_agent_session_v2","slack_rename_conversation","slack_revoke_canvas_access","slack_schedule_message","slack_set_agent_session_status_v2","slack_set_conversation_purpose","slack_set_conversation_topic","slack_set_status","slack_set_suggested_prompts","slack_set_suggested_prompts_v2","slack_set_title","slack_set_user_presence","slack_share_canvas","slack_unarchive_conversation","slack_unfurl_links","slack_unpin_message","slack_update_message","slack_update_user_group","slack_update_user_group_members","slack_update_view","smartlead_add_email_accounts_to_campaign","smartlead_add_leads_to_campaign","smartlead_create_campaign","smartlead_create_lead_list","smartlead_delete_campaign","smartlead_delete_campaign_webhook","smartlead_delete_lead_from_campaign","smartlead_delete_lead_list","smartlead_duplicate_campaign","smartlead_export_campaign_leads","smartlead_get_campaign","smartlead_get_campaign_analytics","smartlead_get_campaign_analytics_by_date","smartlead_get_campaign_lead_statistics","smartlead_get_campaign_mailbox_statistics","smartlead_get_campaign_sequences","smartlead_get_campaign_statistics","smartlead_get_campaign_top_level_analytics_by_date","smartlead_get_campaign_webhook_summary","smartlead_get_lead_by_email","smartlead_get_lead_by_id","smartlead_get_lead_list","smartlead_get_lead_message_history","smartlead_list_campaign_email_accounts","smartlead_list_campaign_leads","smartlead_list_campaign_webhooks","smartlead_list_campaigns","smartlead_list_clients","smartlead_list_email_accounts","smartlead_list_inbox_replies","smartlead_list_lead_activities","smartlead_list_lead_categories","smartlead_list_lead_lists","smartlead_mark_lead_complete","smartlead_pause_lead","smartlead_remove_email_accounts_from_campaign","smartlead_resume_lead","smartlead_save_campaign_sequences","smartlead_unsubscribe_lead_from_campaign","smartlead_unsubscribe_lead_globally","smartlead_update_campaign_schedule","smartlead_update_campaign_settings","smartlead_update_campaign_status","smartlead_update_lead","smartlead_update_lead_category","smartlead_update_lead_list","smartlead_upsert_campaign_webhook","sms_send","smtp_send_mail","snowflake_alter_warehouse","snowflake_call_procedure","snowflake_cancel_statement","snowflake_cancel_task_run","snowflake_cortex_analyst_ask","snowflake_cortex_analyst_feedback","snowflake_delete_rows","snowflake_execute_sql","snowflake_get_statement","snowflake_get_task","snowflake_get_task_run","snowflake_get_task_run_output","snowflake_get_warehouse","snowflake_insert_rows","snowflake_introspect_schema","snowflake_list_copy_history","snowflake_list_databases","snowflake_list_query_history","snowflake_list_schemas","snowflake_list_tables","snowflake_list_task_runs","snowflake_list_tasks","snowflake_list_warehouses","snowflake_load_data","snowflake_resume_task","snowflake_resume_warehouse","snowflake_run_task","snowflake_suspend_task","snowflake_suspend_warehouse","snowflake_unload_data","snowflake_update_rows","snowflake_upsert_rows","splunk_cancel_search_job","splunk_create_search_job","splunk_dispatch_saved_search","splunk_get_fired_alerts","splunk_get_saved_search","splunk_get_search_job","splunk_get_search_results","splunk_list_apps","splunk_list_fired_alerts","splunk_list_indexes","splunk_list_saved_searches","splunk_run_search","sportmonks_core_get_cities","sportmonks_core_get_city","sportmonks_core_get_continent","sportmonks_core_get_continents","sportmonks_core_get_countries","sportmonks_core_get_country","sportmonks_core_get_entity_filters","sportmonks_core_get_my_usage","sportmonks_core_get_region","sportmonks_core_get_regions","sportmonks_core_get_timezones","sportmonks_core_get_type","sportmonks_core_get_type_by_entity","sportmonks_core_get_types","sportmonks_core_search_cities","sportmonks_core_search_countries","sportmonks_core_search_regions","sportmonks_football_expected_by_player","sportmonks_football_expected_by_team","sportmonks_football_get_all_commentaries","sportmonks_football_get_all_fixtures","sportmonks_football_get_all_players","sportmonks_football_get_all_rivals","sportmonks_football_get_all_teams","sportmonks_football_get_all_transfer_rumours","sportmonks_football_get_all_transfers","sportmonks_football_get_brackets_by_season","sportmonks_football_get_coach","sportmonks_football_get_coaches","sportmonks_football_get_coaches_by_country","sportmonks_football_get_commentaries_by_fixture","sportmonks_football_get_current_leagues_by_team","sportmonks_football_get_expected_lineups_by_player","sportmonks_football_get_expected_lineups_by_team","sportmonks_football_get_extended_team_squad","sportmonks_football_get_fixture","sportmonks_football_get_fixtures_by_date","sportmonks_football_get_fixtures_by_date_range","sportmonks_football_get_fixtures_by_date_range_for_team","sportmonks_football_get_fixtures_by_ids","sportmonks_football_get_grouped_standings_by_round","sportmonks_football_get_head_to_head","sportmonks_football_get_inplay_livescores","sportmonks_football_get_latest_coaches","sportmonks_football_get_latest_fixtures","sportmonks_football_get_latest_livescores","sportmonks_football_get_latest_players","sportmonks_football_get_latest_totw","sportmonks_football_get_latest_transfers","sportmonks_football_get_league","sportmonks_football_get_leagues","sportmonks_football_get_leagues_by_country","sportmonks_football_get_leagues_by_date","sportmonks_football_get_leagues_by_team","sportmonks_football_get_live_leagues","sportmonks_football_get_live_probabilities","sportmonks_football_get_live_probabilities_by_fixture","sportmonks_football_get_live_standings_by_league","sportmonks_football_get_livescores","sportmonks_football_get_match_facts","sportmonks_football_get_match_facts_by_date_range","sportmonks_football_get_match_facts_by_fixture","sportmonks_football_get_match_facts_by_league","sportmonks_football_get_past_fixtures_by_tv_station","sportmonks_football_get_player","sportmonks_football_get_players_by_country","sportmonks_football_get_postmatch_news","sportmonks_football_get_postmatch_news_by_season","sportmonks_football_get_predictability_by_league","sportmonks_football_get_prematch_news","sportmonks_football_get_prematch_news_by_season","sportmonks_football_get_prematch_news_upcoming","sportmonks_football_get_probabilities","sportmonks_football_get_probabilities_by_fixture","sportmonks_football_get_referee","sportmonks_football_get_referees","sportmonks_football_get_referees_by_country","sportmonks_football_get_referees_by_season","sportmonks_football_get_rivals_by_team","sportmonks_football_get_round","sportmonks_football_get_round_statistics","sportmonks_football_get_rounds","sportmonks_football_get_rounds_by_season","sportmonks_football_get_schedules_by_season","sportmonks_football_get_schedules_by_season_and_team","sportmonks_football_get_schedules_by_team","sportmonks_football_get_season","sportmonks_football_get_seasons","sportmonks_football_get_seasons_by_team","sportmonks_football_get_stage","sportmonks_football_get_stage_statistics","sportmonks_football_get_stages","sportmonks_football_get_stages_by_season","sportmonks_football_get_standing_corrections_by_season","sportmonks_football_get_standings","sportmonks_football_get_standings_by_round","sportmonks_football_get_standings_by_season","sportmonks_football_get_state","sportmonks_football_get_states","sportmonks_football_get_team","sportmonks_football_get_team_rankings","sportmonks_football_get_team_rankings_by_date","sportmonks_football_get_team_rankings_by_team","sportmonks_football_get_team_squad","sportmonks_football_get_team_squad_by_season","sportmonks_football_get_teams_by_country","sportmonks_football_get_teams_by_season","sportmonks_football_get_topscorers_by_season","sportmonks_football_get_topscorers_by_stage","sportmonks_football_get_totw","sportmonks_football_get_totw_by_round","sportmonks_football_get_transfer","sportmonks_football_get_transfer_rumour","sportmonks_football_get_transfer_rumours_between_dates","sportmonks_football_get_transfer_rumours_by_player","sportmonks_football_get_transfer_rumours_by_team","sportmonks_football_get_transfers_between_dates","sportmonks_football_get_transfers_by_player","sportmonks_football_get_transfers_by_team","sportmonks_football_get_tv_station","sportmonks_football_get_tv_stations","sportmonks_football_get_tv_stations_by_fixture","sportmonks_football_get_upcoming_fixtures_by_market","sportmonks_football_get_upcoming_fixtures_by_tv_station","sportmonks_football_get_value_bets","sportmonks_football_get_value_bets_by_fixture","sportmonks_football_get_venue","sportmonks_football_get_venues","sportmonks_football_get_venues_by_season","sportmonks_football_search_coaches","sportmonks_football_search_fixtures","sportmonks_football_search_leagues","sportmonks_football_search_players","sportmonks_football_search_referees","sportmonks_football_search_rounds","sportmonks_football_search_seasons","sportmonks_football_search_stages","sportmonks_football_search_teams","sportmonks_football_search_venues","sportmonks_motorsport_get_all_fixtures","sportmonks_motorsport_get_current_leagues_by_team","sportmonks_motorsport_get_driver","sportmonks_motorsport_get_driver_standings","sportmonks_motorsport_get_driver_standings_by_season","sportmonks_motorsport_get_drivers","sportmonks_motorsport_get_drivers_by_country","sportmonks_motorsport_get_drivers_by_season","sportmonks_motorsport_get_fixture","sportmonks_motorsport_get_fixtures_by_date","sportmonks_motorsport_get_fixtures_by_date_range","sportmonks_motorsport_get_fixtures_by_ids","sportmonks_motorsport_get_laps_by_fixture","sportmonks_motorsport_get_laps_by_fixture_and_driver","sportmonks_motorsport_get_laps_by_fixture_and_lap","sportmonks_motorsport_get_latest_laps_by_fixture","sportmonks_motorsport_get_latest_pitstops_by_fixture","sportmonks_motorsport_get_latest_stints_by_fixture","sportmonks_motorsport_get_latest_updated_drivers","sportmonks_motorsport_get_latest_updated_fixtures","sportmonks_motorsport_get_league","sportmonks_motorsport_get_leagues","sportmonks_motorsport_get_leagues_by_country","sportmonks_motorsport_get_leagues_by_date","sportmonks_motorsport_get_leagues_by_live","sportmonks_motorsport_get_leagues_by_team","sportmonks_motorsport_get_livescores","sportmonks_motorsport_get_pitstops_by_fixture","sportmonks_motorsport_get_pitstops_by_fixture_and_driver","sportmonks_motorsport_get_pitstops_by_fixture_and_lap","sportmonks_motorsport_get_race_results_by_season_and_driver","sportmonks_motorsport_get_race_results_by_season_and_team","sportmonks_motorsport_get_schedules_by_season","sportmonks_motorsport_get_season","sportmonks_motorsport_get_seasons","sportmonks_motorsport_get_stage","sportmonks_motorsport_get_stages","sportmonks_motorsport_get_stages_by_season","sportmonks_motorsport_get_state","sportmonks_motorsport_get_states","sportmonks_motorsport_get_stints_by_fixture","sportmonks_motorsport_get_stints_by_fixture_and_driver","sportmonks_motorsport_get_stints_by_fixture_and_stint","sportmonks_motorsport_get_team","sportmonks_motorsport_get_team_standings","sportmonks_motorsport_get_team_standings_by_season","sportmonks_motorsport_get_teams","sportmonks_motorsport_get_teams_by_country","sportmonks_motorsport_get_teams_by_season","sportmonks_motorsport_get_venue","sportmonks_motorsport_get_venues","sportmonks_motorsport_get_venues_by_season","sportmonks_motorsport_search_drivers","sportmonks_motorsport_search_leagues","sportmonks_motorsport_search_stages","sportmonks_motorsport_search_teams","sportmonks_motorsport_search_venues","sportmonks_odds_get_all_historical_odds","sportmonks_odds_get_all_inplay_odds","sportmonks_odds_get_all_pre_match_odds","sportmonks_odds_get_all_premium_odds","sportmonks_odds_get_bookmaker","sportmonks_odds_get_bookmaker_event_ids_by_fixture","sportmonks_odds_get_bookmakers","sportmonks_odds_get_bookmakers_by_fixture","sportmonks_odds_get_inplay_odds_by_fixture","sportmonks_odds_get_inplay_odds_by_fixture_and_bookmaker","sportmonks_odds_get_inplay_odds_by_fixture_and_market","sportmonks_odds_get_last_updated_inplay_odds","sportmonks_odds_get_last_updated_pre_match_odds","sportmonks_odds_get_market","sportmonks_odds_get_markets","sportmonks_odds_get_pre_match_odds_by_fixture","sportmonks_odds_get_pre_match_odds_by_fixture_and_bookmaker","sportmonks_odds_get_pre_match_odds_by_fixture_and_market","sportmonks_odds_get_premium_odds_by_fixture","sportmonks_odds_get_premium_odds_by_fixture_and_bookmaker","sportmonks_odds_get_premium_odds_by_fixture_and_market","sportmonks_odds_get_updated_historical_odds_between","sportmonks_odds_get_updated_premium_odds_between","sportmonks_odds_search_bookmakers","sportmonks_odds_search_markets","spotify_add_playlist_cover","spotify_add_to_queue","spotify_add_tracks_to_playlist","spotify_check_following","spotify_check_playlist_followers","spotify_check_saved_albums","spotify_check_saved_audiobooks","spotify_check_saved_episodes","spotify_check_saved_shows","spotify_check_saved_tracks","spotify_create_playlist","spotify_follow_artists","spotify_follow_playlist","spotify_get_album","spotify_get_album_tracks","spotify_get_albums","spotify_get_artist","spotify_get_artist_albums","spotify_get_artist_top_tracks","spotify_get_artists","spotify_get_audiobook","spotify_get_audiobook_chapters","spotify_get_audiobooks","spotify_get_categories","spotify_get_current_user","spotify_get_currently_playing","spotify_get_devices","spotify_get_episode","spotify_get_episodes","spotify_get_followed_artists","spotify_get_markets","spotify_get_new_releases","spotify_get_playback_state","spotify_get_playlist","spotify_get_playlist_cover","spotify_get_playlist_tracks","spotify_get_queue","spotify_get_recently_played","spotify_get_saved_albums","spotify_get_saved_audiobooks","spotify_get_saved_episodes","spotify_get_saved_shows","spotify_get_saved_tracks","spotify_get_show","spotify_get_show_episodes","spotify_get_shows","spotify_get_top_artists","spotify_get_top_tracks","spotify_get_track","spotify_get_tracks","spotify_get_user_playlists","spotify_get_user_profile","spotify_pause","spotify_play","spotify_remove_saved_albums","spotify_remove_saved_audiobooks","spotify_remove_saved_episodes","spotify_remove_saved_shows","spotify_remove_saved_tracks","spotify_remove_tracks_from_playlist","spotify_reorder_playlist_items","spotify_replace_playlist_items","spotify_save_albums","spotify_save_audiobooks","spotify_save_episodes","spotify_save_shows","spotify_save_tracks","spotify_search","spotify_seek","spotify_set_repeat","spotify_set_shuffle","spotify_set_volume","spotify_skip_next","spotify_skip_previous","spotify_transfer_playback","spotify_unfollow_artists","spotify_unfollow_playlist","spotify_update_playlist","sqs_cancel_message_move_task","sqs_change_message_visibility","sqs_change_message_visibility_batch","sqs_create_queue","sqs_delete_message","sqs_delete_message_batch","sqs_delete_queue","sqs_get_queue_attributes","sqs_get_queue_url","sqs_list_dead_letter_source_queues","sqs_list_message_move_tasks","sqs_list_queue_tags","sqs_list_queues","sqs_purge_queue","sqs_receive_message","sqs_send","sqs_send_message_batch","sqs_set_queue_attributes","sqs_start_message_move_task","sqs_tag_queue","sqs_untag_queue","square_batch_retrieve_inventory_counts","square_cancel_invoice","square_cancel_payment","square_complete_payment","square_create_catalog_image","square_create_customer","square_create_invoice","square_create_order","square_create_payment","square_delete_catalog_object","square_delete_customer","square_delete_invoice","square_get_catalog_object","square_get_customer","square_get_invoice","square_get_location","square_get_order","square_get_payment","square_get_refund","square_list_catalog","square_list_customers","square_list_invoices","square_list_locations","square_list_payments","square_list_refunds","square_pay_order","square_publish_invoice","square_refund_payment","square_search_catalog_objects","square_search_customers","square_search_invoices","square_search_orders","square_update_customer","square_upsert_catalog_object","ssh_check_command_exists","ssh_check_file_exists","ssh_create_directory","ssh_delete_file","ssh_download_file","ssh_download_file_v2","ssh_execute_command","ssh_execute_script","ssh_get_system_info","ssh_list_directory","ssh_move_rename","ssh_read_file_content","ssh_upload_file","ssh_write_file_content","ssm_cancel_command","ssm_delete_parameter","ssm_describe_automation_executions","ssm_describe_instance_information","ssm_describe_instance_patch_states","ssm_describe_instance_patches","ssm_describe_parameters","ssm_get_automation_execution","ssm_get_command_invocation","ssm_get_document","ssm_get_parameter","ssm_get_parameters","ssm_get_parameters_by_path","ssm_list_command_invocations","ssm_list_commands","ssm_list_compliance_items","ssm_list_compliance_summaries","ssm_list_documents","ssm_put_parameter","ssm_send_command","ssm_start_automation_execution","ssm_stop_automation_execution","stagehand_agent","stagehand_extract","stripe_cancel_payment_intent","stripe_cancel_subscription","stripe_capture_charge","stripe_capture_payment_intent","stripe_confirm_payment_intent","stripe_create_charge","stripe_create_customer","stripe_create_invoice","stripe_create_payment_intent","stripe_create_price","stripe_create_product","stripe_create_subscription","stripe_delete_customer","stripe_delete_invoice","stripe_delete_product","stripe_finalize_invoice","stripe_list_charges","stripe_list_customers","stripe_list_events","stripe_list_invoices","stripe_list_payment_intents","stripe_list_prices","stripe_list_products","stripe_list_subscriptions","stripe_pay_invoice","stripe_resume_subscription","stripe_retrieve_charge","stripe_retrieve_customer","stripe_retrieve_event","stripe_retrieve_invoice","stripe_retrieve_payment_intent","stripe_retrieve_price","stripe_retrieve_product","stripe_retrieve_subscription","stripe_search_charges","stripe_search_customers","stripe_search_invoices","stripe_search_payment_intents","stripe_search_prices","stripe_search_products","stripe_search_subscriptions","stripe_send_invoice","stripe_update_charge","stripe_update_customer","stripe_update_invoice","stripe_update_payment_intent","stripe_update_price","stripe_update_product","stripe_update_subscription","stripe_void_invoice","sts_assume_role","sts_assume_role_with_saml","sts_assume_role_with_web_identity","sts_get_access_key_info","sts_get_caller_identity","sts_get_session_token","stt_assemblyai","stt_assemblyai_v2","stt_deepgram","stt_deepgram_v2","stt_elevenlabs","stt_elevenlabs_v2","stt_gemini","stt_gemini_v2","stt_whisper","stt_whisper_v2","supabase_count","supabase_delete","supabase_get_row","supabase_insert","supabase_introspect","supabase_invoke_function","supabase_query","supabase_rpc","supabase_storage_copy","supabase_storage_create_bucket","supabase_storage_create_signed_upload_url","supabase_storage_create_signed_url","supabase_storage_delete","supabase_storage_delete_bucket","supabase_storage_download","supabase_storage_empty_bucket","supabase_storage_get_public_url","supabase_storage_list","supabase_storage_list_buckets","supabase_storage_move","supabase_storage_update_bucket","supabase_storage_upload","supabase_text_search","supabase_update","supabase_upsert","supabase_vector_search","table_batch_insert_rows","table_create","table_delete_row","table_delete_rows_by_filter","table_get_row","table_get_schema","table_insert_row","table_list","table_query_rows","table_query_rows_v2","table_update_row","table_update_rows_by_filter","table_upsert_row","tailscale_approve_user","tailscale_authorize_device","tailscale_create_auth_key","tailscale_delete_auth_key","tailscale_delete_device","tailscale_delete_device_attribute","tailscale_delete_user","tailscale_expire_device_key","tailscale_get_acl","tailscale_get_auth_key","tailscale_get_device","tailscale_get_device_attributes","tailscale_get_device_routes","tailscale_get_dns_configuration","tailscale_get_dns_preferences","tailscale_get_dns_searchpaths","tailscale_get_split_dns","tailscale_get_tailnet_settings","tailscale_get_user","tailscale_list_auth_keys","tailscale_list_devices","tailscale_list_dns_nameservers","tailscale_list_users","tailscale_restore_user","tailscale_set_acl","tailscale_set_device_attribute","tailscale_set_device_name","tailscale_set_device_routes","tailscale_set_device_tags","tailscale_set_dns_configuration","tailscale_set_dns_nameservers","tailscale_set_dns_preferences","tailscale_set_dns_searchpaths","tailscale_set_split_dns","tailscale_set_user_role","tailscale_suspend_user","tailscale_update_device_key","tailscale_update_split_dns","tailscale_update_tailnet_settings","tailscale_validate_acl","tavily_crawl","tavily_extract","tavily_map","tavily_search","telegram_copy_message","telegram_delete_message","telegram_edit_message_text","telegram_forward_message","telegram_get_chat","telegram_get_chat_member","telegram_message","telegram_pin_message","telegram_send_animation","telegram_send_audio","telegram_send_chat_action","telegram_send_contact","telegram_send_document","telegram_send_location","telegram_send_photo","telegram_send_poll","telegram_send_video","telegram_set_message_reaction","telegram_unpin_message","temporal_cancel_workflow","temporal_count_workflows","temporal_create_schedule","temporal_delete_schedule","temporal_describe_schedule","temporal_describe_task_queue","temporal_describe_workflow","temporal_get_workflow_history","temporal_list_schedules","temporal_list_workflows","temporal_pause_schedule","temporal_query_workflow","temporal_reset_workflow","temporal_signal_with_start","temporal_signal_workflow","temporal_start_workflow","temporal_terminate_workflow","temporal_trigger_schedule","temporal_unpause_schedule","temporal_update_workflow","textract_analyze_expense","textract_analyze_id","textract_parser","textract_parser_v2","thinking_tool","thrive_add_audience_managers","thrive_add_audience_members","thrive_add_user_tags","thrive_create_assignment","thrive_create_audience","thrive_create_completion","thrive_create_user","thrive_delete_assignment","thrive_delete_audience","thrive_delete_user","thrive_get_activity","thrive_get_assignment","thrive_get_audience","thrive_get_completion","thrive_get_content","thrive_get_cpd_category","thrive_get_cpd_entry","thrive_get_cpd_requirement","thrive_get_enrolment","thrive_get_skill_levels","thrive_get_tag","thrive_get_user_by_id","thrive_get_user_by_ref","thrive_list_assignments","thrive_list_audience_managers","thrive_list_audience_members","thrive_list_audiences","thrive_list_completions","thrive_list_enrolments","thrive_list_tags","thrive_query_activities","thrive_query_content","thrive_query_cpd_categories","thrive_query_cpd_entries","thrive_query_cpd_requirements","thrive_query_cpd_user_summaries","thrive_remove_audience_manager","thrive_remove_audience_member","thrive_remove_user_tags","thrive_replace_audience_managers","thrive_replace_audience_members","thrive_search_users","thrive_suspend_user","thrive_update_assignment","thrive_update_audience","thrive_update_user","thrive_update_user_skills","tiktok_get_post_status","tiktok_get_user","tiktok_list_videos","tiktok_query_videos","tiktok_upload_video_draft","tinybird_append_datasource","tinybird_delete_datasource_rows","tinybird_events","tinybird_get_job","tinybird_query","tinybird_query_pipe","tinybird_truncate_datasource","tinyfish_cancel_run","tinyfish_fetch","tinyfish_get_run","tinyfish_list_profiles","tinyfish_list_runs","tinyfish_list_vault_items","tinyfish_run","tinyfish_run_async","tinyfish_search","trello_add_checklist","trello_add_checklist_item","trello_add_comment","trello_add_label","trello_add_member","trello_create_board","trello_create_card","trello_create_list","trello_delete_card","trello_get_actions","trello_get_board","trello_get_card","trello_list_cards","trello_list_lists","trello_list_members","trello_remove_label","trello_remove_member","trello_search","trello_update_card","trello_update_checklist_item","trello_update_list","trigger_dev_activate_schedule","trigger_dev_add_run_tags","trigger_dev_batch_trigger_task","trigger_dev_cancel_run","trigger_dev_complete_waitpoint_token","trigger_dev_create_env_var","trigger_dev_create_schedule","trigger_dev_create_waitpoint_token","trigger_dev_deactivate_schedule","trigger_dev_delete_env_var","trigger_dev_delete_schedule","trigger_dev_execute_query","trigger_dev_get_batch","trigger_dev_get_batch_results","trigger_dev_get_deployment","trigger_dev_get_env_var","trigger_dev_get_latest_deployment","trigger_dev_get_query_schema","trigger_dev_get_queue","trigger_dev_get_run","trigger_dev_get_run_events","trigger_dev_get_run_result","trigger_dev_get_run_trace","trigger_dev_get_schedule","trigger_dev_get_waitpoint_token","trigger_dev_import_env_vars","trigger_dev_list_deployments","trigger_dev_list_env_vars","trigger_dev_list_queues","trigger_dev_list_runs","trigger_dev_list_schedules","trigger_dev_list_timezones","trigger_dev_list_waitpoint_tokens","trigger_dev_override_queue_concurrency","trigger_dev_pause_queue","trigger_dev_promote_deployment","trigger_dev_replay_run","trigger_dev_reschedule_run","trigger_dev_reset_queue_concurrency","trigger_dev_resume_queue","trigger_dev_trigger_task","trigger_dev_update_env_var","trigger_dev_update_run_metadata","trigger_dev_update_schedule","tts_azure","tts_cartesia","tts_deepgram","tts_elevenlabs","tts_google","tts_openai","tts_playht","twilio_send_sms","twilio_voice_get_recording","twilio_voice_list_calls","twilio_voice_make_call","typeform_create_form","typeform_delete_form","typeform_files","typeform_get_form","typeform_insights","typeform_list_forms","typeform_responses","typeform_update_form","upstash_redis_command","upstash_redis_delete","upstash_redis_exists","upstash_redis_expire","upstash_redis_get","upstash_redis_hget","upstash_redis_hgetall","upstash_redis_hset","upstash_redis_incr","upstash_redis_incrby","upstash_redis_keys","upstash_redis_lpush","upstash_redis_lrange","upstash_redis_set","upstash_redis_setnx","upstash_redis_ttl","uptimerobot_create_alert_contact","uptimerobot_create_maintenance_window","uptimerobot_create_monitor","uptimerobot_create_psp","uptimerobot_delete_alert_contact","uptimerobot_delete_maintenance_window","uptimerobot_delete_monitor","uptimerobot_delete_psp","uptimerobot_get_account","uptimerobot_get_alert_contact","uptimerobot_get_incident","uptimerobot_get_maintenance_window","uptimerobot_get_monitor","uptimerobot_get_psp","uptimerobot_list_alert_contacts","uptimerobot_list_incidents","uptimerobot_list_maintenance_windows","uptimerobot_list_monitors","uptimerobot_list_psps","uptimerobot_pause_monitor","uptimerobot_start_monitor","uptimerobot_update_maintenance_window","uptimerobot_update_monitor","uptimerobot_update_psp","vanta_download_document_file","vanta_get_control","vanta_get_document","vanta_get_framework","vanta_get_person","vanta_get_policy","vanta_get_risk_scenario","vanta_get_test","vanta_get_vendor","vanta_get_vulnerable_asset","vanta_list_control_documents","vanta_list_control_tests","vanta_list_controls","vanta_list_document_uploads","vanta_list_documents","vanta_list_framework_controls","vanta_list_frameworks","vanta_list_monitored_computers","vanta_list_people","vanta_list_policies","vanta_list_risk_scenarios","vanta_list_test_entities","vanta_list_tests","vanta_list_vendors","vanta_list_vulnerabilities","vanta_list_vulnerability_remediations","vanta_list_vulnerable_assets","vanta_submit_document","vanta_upload_document_file","vercel_add_domain","vercel_add_project_domain","vercel_cancel_deployment","vercel_create_alias","vercel_create_check","vercel_create_deployment","vercel_create_dns_record","vercel_create_edge_config","vercel_create_env_var","vercel_create_project","vercel_create_webhook","vercel_delete_alias","vercel_delete_deployment","vercel_delete_dns_record","vercel_delete_domain","vercel_delete_edge_config","vercel_delete_env_var","vercel_delete_project","vercel_delete_webhook","vercel_get_alias","vercel_get_check","vercel_get_deployment","vercel_get_deployment_events","vercel_get_domain","vercel_get_domain_config","vercel_get_edge_config","vercel_get_edge_config_items","vercel_get_env_vars","vercel_get_project","vercel_get_team","vercel_get_user","vercel_get_webhook","vercel_list_aliases","vercel_list_checks","vercel_list_deployment_files","vercel_list_deployments","vercel_list_dns_records","vercel_list_domains","vercel_list_edge_configs","vercel_list_project_domains","vercel_list_projects","vercel_list_team_members","vercel_list_teams","vercel_list_webhooks","vercel_pause_project","vercel_promote_deployment","vercel_remove_project_domain","vercel_rerequest_check","vercel_unpause_project","vercel_update_check","vercel_update_dns_record","vercel_update_edge_config_items","vercel_update_env_var","vercel_update_project","vercel_update_project_domain","vercel_verify_project_domain","video_falai","video_higgsfield","video_luma","video_minimax","video_runway","video_veo","vision_tool","vision_tool_v2","wealthbox_read_contact","wealthbox_read_note","wealthbox_read_task","wealthbox_write_contact","wealthbox_write_note","wealthbox_write_task","webflow_create_item","webflow_delete_item","webflow_get_item","webflow_list_items","webflow_update_item","webhook_request","whatsapp_get_media","whatsapp_mark_read","whatsapp_send_interactive","whatsapp_send_media","whatsapp_send_message","whatsapp_send_reaction","whatsapp_send_template","whatsapp_upload_media","wikipedia_content","wikipedia_random","wikipedia_search","wikipedia_summary","windchill_check_in_document","windchill_check_in_documents","windchill_check_out_document","windchill_check_out_documents","windchill_create_document","windchill_create_documents","windchill_delete_document","windchill_delete_documents","windchill_download_attachment","windchill_download_primary_content","windchill_get_document","windchill_get_document_structure","windchill_get_primary_content","windchill_get_valid_state_transitions","windchill_list_attachments","windchill_list_documents","windchill_revise_document","windchill_revise_documents","windchill_set_lifecycle_state","windchill_undo_check_out_document","windchill_undo_check_out_documents","windchill_update_common_properties","windchill_update_document","windchill_update_document_security_labels","windchill_update_documents","windchill_upload_attachments","windchill_upload_primary_content","wiza_company_enrichment","wiza_get_credits","wiza_individual_reveal","wiza_prospect_search","wordpress_create_category","wordpress_create_comment","wordpress_create_page","wordpress_create_post","wordpress_create_tag","wordpress_delete_category","wordpress_delete_comment","wordpress_delete_media","wordpress_delete_page","wordpress_delete_post","wordpress_delete_tag","wordpress_get_category","wordpress_get_current_user","wordpress_get_media","wordpress_get_page","wordpress_get_post","wordpress_get_tag","wordpress_get_user","wordpress_list_categories","wordpress_list_comments","wordpress_list_media","wordpress_list_pages","wordpress_list_posts","wordpress_list_tags","wordpress_list_users","wordpress_search_content","wordpress_update_category","wordpress_update_comment","wordpress_update_page","wordpress_update_post","wordpress_update_tag","wordpress_upload_media","workday_assign_onboarding","workday_change_job","workday_create_prehire","workday_get_compensation","workday_get_organizations","workday_get_worker","workday_hire_employee","workday_list_workers","workday_terminate_worker","workday_update_worker","workflow_executor","x_create_bookmark","x_create_tweet","x_delete_bookmark","x_delete_tweet","x_get_blocking","x_get_bookmarks","x_get_followers","x_get_following","x_get_liked_tweets","x_get_liking_users","x_get_me","x_get_personalized_trends","x_get_quote_tweets","x_get_retweeted_by","x_get_trends_by_woeid","x_get_tweets_by_ids","x_get_usage","x_get_user_mentions","x_get_user_timeline","x_get_user_tweets","x_hide_reply","x_manage_block","x_manage_follow","x_manage_like","x_manage_mute","x_manage_retweet","x_read","x_search","x_search_tweets","x_search_users","x_user","x_write","youcom_answer","youcom_finance_research","youcom_get_account_balance","youcom_get_contents","youcom_get_research_task","youcom_research","youcom_search","youcom_search_images","youtube_channel_info","youtube_channel_playlists","youtube_channel_videos","youtube_comments","youtube_playlist_items","youtube_search","youtube_trending","youtube_video_categories","youtube_video_details","zendesk_autocomplete_organizations","zendesk_create_organization","zendesk_create_organizations_bulk","zendesk_create_ticket","zendesk_create_tickets_bulk","zendesk_create_user","zendesk_create_users_bulk","zendesk_delete_organization","zendesk_delete_ticket","zendesk_delete_user","zendesk_get_current_user","zendesk_get_organization","zendesk_get_organizations","zendesk_get_ticket","zendesk_get_tickets","zendesk_get_user","zendesk_get_users","zendesk_merge_tickets","zendesk_search","zendesk_search_count","zendesk_search_users","zendesk_update_organization","zendesk_update_ticket","zendesk_update_tickets_bulk","zendesk_update_user","zendesk_update_users_bulk","zep_add_messages","zep_add_user","zep_create_thread","zep_delete_thread","zep_get_context","zep_get_messages","zep_get_threads","zep_get_user","zep_get_user_threads","zerobounce_get_credits","zerobounce_verify_email","zoho_desk_add_comment","zoho_desk_create_account","zoho_desk_create_contact","zoho_desk_create_ticket","zoho_desk_delete_comment","zoho_desk_get_account","zoho_desk_get_agent","zoho_desk_get_attachment","zoho_desk_get_comment","zoho_desk_get_contact","zoho_desk_get_department","zoho_desk_get_thread","zoho_desk_get_ticket","zoho_desk_list_accounts","zoho_desk_list_agents","zoho_desk_list_comments","zoho_desk_list_contacts","zoho_desk_list_departments","zoho_desk_list_organizations","zoho_desk_list_threads","zoho_desk_list_tickets","zoho_desk_move_ticket","zoho_desk_search_accounts","zoho_desk_search_contacts","zoho_desk_search_tickets","zoho_desk_send_reply","zoho_desk_update_account","zoho_desk_update_comment","zoho_desk_update_contact","zoho_desk_update_ticket","zoom_create_meeting","zoom_delete_meeting","zoom_delete_recording","zoom_get_meeting","zoom_get_meeting_invitation","zoom_get_meeting_recordings","zoom_list_meetings","zoom_list_past_participants","zoom_list_recordings","zoom_update_meeting","zoominfo_enrich_companies","zoominfo_enrich_contacts","zoominfo_search_companies","zoominfo_search_contacts","zoominfo_search_intent","zoominfo_search_news"]' + '["a2a_cancel_task","a2a_get_agent_card","a2a_get_task","a2a_send_message","affinity_batch_update_entity_fields","affinity_batch_update_list_entry_fields","affinity_create_list","affinity_create_list_field_dropdown_option","affinity_create_merge","affinity_create_note","affinity_create_reminder","affinity_delete_list_field_dropdown_option","affinity_delete_note","affinity_get_company","affinity_get_current_user","affinity_get_entity_field_value","affinity_get_list","affinity_get_list_entry","affinity_get_list_entry_field","affinity_get_list_field_dropdown_option","affinity_get_merge","affinity_get_merge_task","affinity_get_note","affinity_get_opportunity","affinity_get_person","affinity_get_saved_view","affinity_get_transcript","affinity_get_user","affinity_list_calls","affinity_list_chat_messages","affinity_list_companies","affinity_list_coworker_connections","affinity_list_emails","affinity_list_entity_field_values","affinity_list_entity_list_entries","affinity_list_entity_lists","affinity_list_entity_notes","affinity_list_entity_relationships","affinity_list_field_dropdown_options","affinity_list_field_metadata","affinity_list_field_value_changes","affinity_list_investor_executive_connections","affinity_list_list_entries","affinity_list_list_entry_field_value_changes","affinity_list_list_entry_fields","affinity_list_list_field_dropdown_options","affinity_list_list_fields","affinity_list_lists","affinity_list_meetings","affinity_list_merge_tasks","affinity_list_merges","affinity_list_note_attached_companies","affinity_list_note_attached_opportunities","affinity_list_note_attached_persons","affinity_list_note_replies","affinity_list_notes","affinity_list_opportunities","affinity_list_persons","affinity_list_reminders","affinity_list_saved_view_entries","affinity_list_saved_views","affinity_list_transcript_fragments","affinity_list_transcripts","affinity_list_users","affinity_search_companies","affinity_search_files","affinity_search_list_entries","affinity_search_notes","affinity_search_persons","affinity_semantic_search","affinity_update_entity_field_value","affinity_update_list_entry_field","affinity_update_list_field_dropdown_option","affinity_update_note","agentmail_create_draft","agentmail_create_inbox","agentmail_delete_draft","agentmail_delete_inbox","agentmail_delete_thread","agentmail_forward_message","agentmail_get_draft","agentmail_get_inbox","agentmail_get_message","agentmail_get_thread","agentmail_list_drafts","agentmail_list_inboxes","agentmail_list_messages","agentmail_list_threads","agentmail_reply_message","agentmail_send_draft","agentmail_send_message","agentmail_update_draft","agentmail_update_inbox","agentmail_update_message","agentmail_update_thread","agentphone_create_call","agentphone_create_contact","agentphone_create_number","agentphone_delete_contact","agentphone_get_call","agentphone_get_call_transcript","agentphone_get_contact","agentphone_get_conversation","agentphone_get_conversation_messages","agentphone_get_number_messages","agentphone_get_usage","agentphone_get_usage_daily","agentphone_get_usage_monthly","agentphone_list_calls","agentphone_list_contacts","agentphone_list_conversations","agentphone_list_numbers","agentphone_react_to_message","agentphone_release_number","agentphone_send_message","agentphone_update_contact","agentphone_update_conversation","agiloft_async_status","agiloft_attach_file","agiloft_attachment_info","agiloft_create_record","agiloft_delete_record","agiloft_get_choice_line_id","agiloft_list_tables","agiloft_lock_record","agiloft_nlp_search","agiloft_read_record","agiloft_remove_attachment","agiloft_retrieve_attachment","agiloft_run_action_button","agiloft_saved_search","agiloft_search_records","agiloft_select_records","agiloft_update_record","agiloft_upsert_record","ahrefs_anchors","ahrefs_backlinks","ahrefs_backlinks_stats","ahrefs_batch_analysis","ahrefs_broken_backlinks","ahrefs_domain_rating","ahrefs_domain_rating_history","ahrefs_keyword_overview","ahrefs_keywords_history","ahrefs_metrics","ahrefs_metrics_history","ahrefs_organic_competitors","ahrefs_organic_keywords","ahrefs_paid_pages","ahrefs_rank_tracker_competitors_overview","ahrefs_rank_tracker_competitors_stats","ahrefs_rank_tracker_overview","ahrefs_rank_tracker_serp_overview","ahrefs_refdomains_history","ahrefs_referring_domains","ahrefs_related_terms","ahrefs_site_audit_page_explorer","ahrefs_top_pages","airtable_create_records","airtable_delete_records","airtable_get_base_schema","airtable_get_record","airtable_list_bases","airtable_list_records","airtable_list_tables","airtable_update_multiple_records","airtable_update_record","airtable_upsert_records","algolia_add_record","algolia_batch_operations","algolia_browse_records","algolia_clear_records","algolia_copy_move_index","algolia_delete_by_filter","algolia_delete_index","algolia_delete_record","algolia_get_record","algolia_get_records","algolia_get_settings","algolia_get_task_status","algolia_list_indices","algolia_partial_update_record","algolia_search","algolia_update_settings","amplitude_event_segmentation","amplitude_funnels","amplitude_get_active_users","amplitude_get_revenue","amplitude_group_identify","amplitude_identify_user","amplitude_list_events","amplitude_realtime_active_users","amplitude_retention","amplitude_send_event","amplitude_user_activity","amplitude_user_profile","amplitude_user_search","apify_get_dataset_items","apify_get_run","apify_run_actor_async","apify_run_actor_sync","apify_run_task","apollo_account_bulk_create","apollo_account_bulk_update","apollo_account_create","apollo_account_search","apollo_account_update","apollo_contact_bulk_create","apollo_contact_bulk_update","apollo_contact_create","apollo_contact_search","apollo_contact_update","apollo_email_accounts","apollo_opportunity_create","apollo_opportunity_get","apollo_opportunity_search","apollo_opportunity_update","apollo_organization_bulk_enrich","apollo_organization_enrich","apollo_organization_search","apollo_people_bulk_enrich","apollo_people_enrich","apollo_people_search","apollo_sequence_add_contacts","apollo_sequence_search","apollo_task_create","apollo_task_search","appconfig_create_application","appconfig_create_configuration_profile","appconfig_create_environment","appconfig_create_hosted_configuration_version","appconfig_delete_application","appconfig_delete_configuration_profile","appconfig_delete_environment","appconfig_delete_hosted_configuration_version","appconfig_get_application","appconfig_get_configuration","appconfig_get_configuration_profile","appconfig_get_deployment","appconfig_get_environment","appconfig_get_hosted_configuration_version","appconfig_list_applications","appconfig_list_configuration_profiles","appconfig_list_deployment_strategies","appconfig_list_deployments","appconfig_list_environments","appconfig_list_hosted_configuration_versions","appconfig_start_deployment","appconfig_stop_deployment","appconfig_update_application","appconfig_update_configuration_profile","appconfig_update_environment","arxiv_get_author_papers","arxiv_get_paper","arxiv_search","asana_add_comment","asana_add_followers","asana_create_project","asana_create_section","asana_create_subtask","asana_create_task","asana_delete_task","asana_get_project","asana_get_projects","asana_get_task","asana_list_sections","asana_list_workspaces","asana_search_tasks","asana_update_task","ashby_add_candidate_tag","ashby_anonymize_candidate","ashby_change_application_source","ashby_change_application_stage","ashby_create_application","ashby_create_candidate","ashby_create_note","ashby_delete_application","ashby_get_application","ashby_get_candidate","ashby_get_job","ashby_get_job_posting","ashby_get_offer","ashby_get_opening","ashby_list_application_feedback","ashby_list_application_history","ashby_list_applications","ashby_list_archive_reasons","ashby_list_candidate_tags","ashby_list_candidates","ashby_list_custom_fields","ashby_list_departments","ashby_list_interview_plans","ashby_list_interview_stages","ashby_list_interviews","ashby_list_job_postings","ashby_list_jobs","ashby_list_locations","ashby_list_notes","ashby_list_offers","ashby_list_openings","ashby_list_sources","ashby_list_users","ashby_remove_candidate_tag","ashby_search_candidates","ashby_search_jobs","ashby_search_openings","ashby_search_users","ashby_set_custom_field_value","ashby_set_custom_field_values","ashby_transfer_application","ashby_update_candidate","ashby_upload_candidate_file","ashby_upload_resume","athena_batch_get_named_query","athena_batch_get_prepared_statement","athena_batch_get_query_execution","athena_create_named_query","athena_create_prepared_statement","athena_delete_named_query","athena_delete_prepared_statement","athena_get_data_catalog","athena_get_database","athena_get_named_query","athena_get_prepared_statement","athena_get_query_execution","athena_get_query_results","athena_get_query_runtime_statistics","athena_get_table_metadata","athena_get_work_group","athena_list_data_catalogs","athena_list_databases","athena_list_named_queries","athena_list_prepared_statements","athena_list_query_executions","athena_list_table_metadata","athena_list_work_groups","athena_start_query","athena_stop_query","athena_update_named_query","athena_update_prepared_statement","attio_assert_record","attio_create_attribute","attio_create_comment","attio_create_list","attio_create_list_entry","attio_create_note","attio_create_object","attio_create_record","attio_create_task","attio_create_webhook","attio_delete_comment","attio_delete_list_entry","attio_delete_note","attio_delete_record","attio_delete_task","attio_delete_webhook","attio_get_attribute","attio_get_comment","attio_get_list","attio_get_list_entry","attio_get_member","attio_get_note","attio_get_object","attio_get_record","attio_get_task","attio_get_thread","attio_get_webhook","attio_list_attributes","attio_list_lists","attio_list_members","attio_list_notes","attio_list_objects","attio_list_records","attio_list_tasks","attio_list_threads","attio_list_webhooks","attio_query_list_entries","attio_search_records","attio_update_attribute","attio_update_list","attio_update_list_entry","attio_update_object","attio_update_record","attio_update_task","attio_update_webhook","azure_data_explorer_create_table","azure_data_explorer_drop_table","azure_data_explorer_ingest_from_query","azure_data_explorer_ingest_inline","azure_data_explorer_list_databases","azure_data_explorer_list_functions","azure_data_explorer_list_tables","azure_data_explorer_management","azure_data_explorer_query","azure_data_explorer_show_database_schema","azure_data_explorer_show_ingestion_failures","azure_data_explorer_show_operations","azure_data_explorer_show_table_details","azure_data_explorer_show_table_schema","azure_devops_add_comment","azure_devops_create_work_item","azure_devops_get_build_log","azure_devops_get_build_timeline","azure_devops_get_comments","azure_devops_get_pipeline","azure_devops_get_pipeline_run","azure_devops_get_work_item","azure_devops_get_work_items_batch","azure_devops_get_work_items_between_builds","azure_devops_list_build_logs","azure_devops_list_builds","azure_devops_list_pipeline_runs","azure_devops_list_pipelines","azure_devops_query_work_items","azure_devops_update_work_item","bitbucket_approve_pull_request","bitbucket_create_branch","bitbucket_create_pull_request","bitbucket_create_pull_request_comment","bitbucket_decline_pull_request","bitbucket_delete_branch","bitbucket_get_commit","bitbucket_get_file","bitbucket_get_file_metadata","bitbucket_get_pipeline","bitbucket_get_pipeline_step_log","bitbucket_get_pull_request","bitbucket_get_pull_request_diff","bitbucket_get_pull_request_diffstat","bitbucket_get_pull_request_merge_task_status","bitbucket_get_repository","bitbucket_list_branches","bitbucket_list_commits","bitbucket_list_directory","bitbucket_list_pipeline_steps","bitbucket_list_pipelines","bitbucket_list_pull_request_comments","bitbucket_list_pull_request_commit_statuses","bitbucket_list_pull_requests","bitbucket_list_repositories","bitbucket_list_workspaces","bitbucket_merge_pull_request","bitbucket_request_pull_request_changes","bitbucket_stop_pipeline","bitbucket_trigger_pipeline","box_copy_file","box_create_folder","box_delete_file","box_delete_folder","box_download_file","box_download_file_v2","box_get_file_info","box_list_folder_items","box_search","box_sign_cancel_request","box_sign_create_request","box_sign_get_request","box_sign_list_requests","box_sign_resend_request","box_update_file","box_upload_file","brandfetch_get_brand","brandfetch_search","brex_archive_budget","brex_create_budget","brex_create_spend_limit","brex_create_transfer","brex_create_vendor","brex_get_budget","brex_get_cash_account","brex_get_company","brex_get_current_user","brex_get_expense","brex_get_spend_limit","brex_get_transfer","brex_get_user","brex_get_vendor","brex_list_budgets","brex_list_card_accounts","brex_list_card_statements","brex_list_card_transactions","brex_list_cards","brex_list_cash_accounts","brex_list_cash_statements","brex_list_cash_transactions","brex_list_departments","brex_list_expenses","brex_list_locations","brex_list_spend_limits","brex_list_titles","brex_list_transfers","brex_list_users","brex_list_vendors","brex_match_receipt","brex_update_expense","brex_update_vendor","brex_upload_receipt","brightdata_cancel_snapshot","brightdata_discover","brightdata_download_snapshot","brightdata_scrape_dataset","brightdata_scrape_url","brightdata_serp_search","brightdata_snapshot_status","brightdata_sync_scrape","browser_use_run_task","buffer_create_idea","buffer_create_post","buffer_delete_post","buffer_edit_post","buffer_get_account","buffer_get_aggregated_post_metrics","buffer_get_channel","buffer_get_channels","buffer_get_daily_posting_limits","buffer_get_idea_groups","buffer_get_ideas","buffer_get_post","buffer_get_posts","calcom_cancel_booking","calcom_confirm_booking","calcom_create_booking","calcom_create_event_type","calcom_create_schedule","calcom_decline_booking","calcom_delete_event_type","calcom_delete_schedule","calcom_get_booking","calcom_get_default_schedule","calcom_get_event_type","calcom_get_schedule","calcom_get_slots","calcom_list_bookings","calcom_list_event_types","calcom_list_schedules","calcom_reschedule_booking","calcom_update_event_type","calcom_update_schedule","calendly_cancel_event","calendly_create_event_invitee","calendly_create_invitee_no_show","calendly_create_scheduling_link","calendly_create_webhook","calendly_delete_invitee_no_show","calendly_delete_webhook","calendly_get_current_user","calendly_get_event_invitee","calendly_get_event_type","calendly_get_scheduled_event","calendly_get_user","calendly_list_event_invitees","calendly_list_event_type_available_times","calendly_list_event_types","calendly_list_organization_memberships","calendly_list_routing_form_submissions","calendly_list_routing_forms","calendly_list_scheduled_events","calendly_list_user_availability_schedules","calendly_list_user_busy_times","calendly_list_webhooks","cbinsights_chat","cbinsights_get_commercial_maturity_history","cbinsights_get_exit_probability_history","cbinsights_get_mosaic_history","cbinsights_get_org_business_relationships","cbinsights_get_org_funding_window","cbinsights_get_org_fundings","cbinsights_get_org_investments","cbinsights_get_org_management_and_board","cbinsights_get_org_outlook","cbinsights_get_org_portfolio_exits","cbinsights_get_org_revenue","cbinsights_get_scouting_report","cbinsights_get_strategy_map","cbinsights_list_business_relationships","cbinsights_list_funding_window","cbinsights_list_fundings","cbinsights_list_investments","cbinsights_list_management_and_board","cbinsights_list_outlook","cbinsights_list_portfolio_exits","cbinsights_list_revenue","cbinsights_lookup_organizations","cbinsights_rag","cbinsights_search_firmographics","checkr_add_report_tag","checkr_apply_report_review_action","checkr_cancel_adverse_action","checkr_cancel_continuous_check","checkr_cancel_invitation","checkr_cancel_subscription","checkr_complete_report","checkr_create_adverse_action","checkr_create_candidate","checkr_create_continuous_check","checkr_create_geo","checkr_create_invitation","checkr_create_report","checkr_create_subscription","checkr_delete_candidate_pii","checkr_delete_geo","checkr_get_account","checkr_get_adverse_action","checkr_get_candidate","checkr_get_continuous_check","checkr_get_document","checkr_get_geo","checkr_get_invitation","checkr_get_node","checkr_get_package","checkr_get_program","checkr_get_report","checkr_get_report_eta","checkr_get_report_progressive_status","checkr_get_report_tags","checkr_get_screening","checkr_get_subscription","checkr_get_verification","checkr_list_adverse_actions","checkr_list_adverse_items","checkr_list_assessments","checkr_list_candidate_documents","checkr_list_candidates","checkr_list_continuous_checks","checkr_list_counties","checkr_list_geos","checkr_list_invitations","checkr_list_nodes","checkr_list_packages","checkr_list_programs","checkr_list_report_addresses","checkr_list_subscriptions","checkr_list_users","checkr_list_verifications","checkr_remove_report_tag","checkr_set_report_tags","checkr_update_candidate","checkr_update_continuous_check","checkr_update_geo","checkr_update_report","checkr_update_subscription","circleback_add_tag_to_meetings","circleback_create_tag","circleback_delete_action_item","circleback_delete_meeting","circleback_delete_tag","circleback_get_company","circleback_get_meeting","circleback_get_person","circleback_get_transcript","circleback_list_action_items","circleback_list_calendar_events","circleback_list_companies","circleback_list_meetings","circleback_list_people","circleback_list_tags","circleback_remove_tag_from_meetings","circleback_search_meetings","circleback_update_action_item","circleback_update_meeting","circleback_update_tag","clay_populate","clerk_add_organization_member","clerk_ban_user","clerk_create_actor_token","clerk_create_allowlist_identifier","clerk_create_blocklist_identifier","clerk_create_organization","clerk_create_organization_invitation","clerk_create_user","clerk_delete_allowlist_identifier","clerk_delete_blocklist_identifier","clerk_delete_organization","clerk_delete_user","clerk_get_jwt_template","clerk_get_organization","clerk_get_session","clerk_get_user","clerk_get_user_oauth_token","clerk_list_allowlist_identifiers","clerk_list_blocklist_identifiers","clerk_list_jwt_templates","clerk_list_organization_invitations","clerk_list_organization_memberships","clerk_list_organizations","clerk_list_sessions","clerk_list_users","clerk_lock_user","clerk_remove_organization_member","clerk_revoke_actor_token","clerk_revoke_session","clerk_unban_user","clerk_unlock_user","clerk_update_organization","clerk_update_organization_membership","clerk_update_user","clickhouse_count_rows","clickhouse_create_database","clickhouse_create_table","clickhouse_delete","clickhouse_describe_table","clickhouse_drop_database","clickhouse_drop_partition","clickhouse_drop_table","clickhouse_execute","clickhouse_insert","clickhouse_insert_rows","clickhouse_introspect","clickhouse_kill_query","clickhouse_list_clusters","clickhouse_list_databases","clickhouse_list_mutations","clickhouse_list_partitions","clickhouse_list_running_queries","clickhouse_list_tables","clickhouse_optimize_table","clickhouse_query","clickhouse_rename_table","clickhouse_show_create_table","clickhouse_table_stats","clickhouse_truncate_table","clickhouse_update","clickup_add_tag_to_task","clickup_create_checklist","clickup_create_checklist_item","clickup_create_comment","clickup_create_folder","clickup_create_list","clickup_create_task","clickup_create_time_entry","clickup_delete_checklist","clickup_delete_checklist_item","clickup_delete_comment","clickup_delete_task","clickup_delete_time_entry","clickup_get_comments","clickup_get_custom_fields","clickup_get_folders","clickup_get_list_members","clickup_get_lists","clickup_get_running_timer","clickup_get_space_tags","clickup_get_spaces","clickup_get_task","clickup_get_task_members","clickup_get_tasks","clickup_get_time_entries","clickup_get_workspaces","clickup_remove_custom_field_value","clickup_remove_tag_from_task","clickup_search_tasks","clickup_set_custom_field_value","clickup_start_timer","clickup_stop_timer","clickup_update_checklist","clickup_update_checklist_item","clickup_update_comment","clickup_update_task","clickup_update_time_entry","clickup_upload_attachment","cloudflare_create_access_application","cloudflare_create_access_group","cloudflare_create_access_policy","cloudflare_create_access_service_token","cloudflare_create_dns_record","cloudflare_create_r2_bucket","cloudflare_create_rate_limit_rule","cloudflare_create_ruleset","cloudflare_create_ruleset_rule","cloudflare_create_tunnel","cloudflare_create_worker_route","cloudflare_create_zone","cloudflare_delete_access_application","cloudflare_delete_access_group","cloudflare_delete_access_policy","cloudflare_delete_dns_record","cloudflare_delete_r2_bucket","cloudflare_delete_r2_bucket_cors","cloudflare_delete_ruleset","cloudflare_delete_ruleset_rule","cloudflare_delete_tunnel","cloudflare_delete_worker_route","cloudflare_delete_worker_script","cloudflare_delete_zone","cloudflare_dns_analytics","cloudflare_get_access_application","cloudflare_get_access_group","cloudflare_get_access_policy","cloudflare_get_access_service_token","cloudflare_get_dns_record","cloudflare_get_r2_bucket","cloudflare_get_r2_bucket_cors","cloudflare_get_r2_bucket_lifecycle","cloudflare_get_ruleset","cloudflare_get_ruleset_entrypoint","cloudflare_get_tunnel","cloudflare_get_tunnel_configuration","cloudflare_get_tunnel_token","cloudflare_get_worker_route","cloudflare_get_worker_script_settings","cloudflare_get_zone","cloudflare_get_zone_settings","cloudflare_list_access_applications","cloudflare_list_access_groups","cloudflare_list_access_identity_providers","cloudflare_list_access_policies","cloudflare_list_access_service_tokens","cloudflare_list_certificates","cloudflare_list_dns_records","cloudflare_list_managed_ruleset_overrides","cloudflare_list_r2_buckets","cloudflare_list_rate_limit_rules","cloudflare_list_rulesets","cloudflare_list_tunnels","cloudflare_list_worker_routes","cloudflare_list_worker_scripts","cloudflare_list_zones","cloudflare_purge_cache","cloudflare_refresh_access_service_token","cloudflare_revoke_access_service_token","cloudflare_rotate_access_service_token","cloudflare_update_access_application","cloudflare_update_access_group","cloudflare_update_access_policy","cloudflare_update_access_service_token","cloudflare_update_dns_record","cloudflare_update_r2_bucket_cors","cloudflare_update_r2_bucket_lifecycle","cloudflare_update_rate_limit_rule","cloudflare_update_ruleset","cloudflare_update_ruleset_rule","cloudflare_update_tunnel","cloudflare_update_tunnel_configuration","cloudflare_update_worker_route","cloudflare_update_zone","cloudflare_update_zone_setting","cloudformation_cancel_update_stack","cloudformation_create_change_set","cloudformation_create_stack","cloudformation_delete_stack","cloudformation_describe_change_set","cloudformation_describe_stack_drift_detection_status","cloudformation_describe_stack_events","cloudformation_describe_stacks","cloudformation_detect_stack_drift","cloudformation_execute_change_set","cloudformation_get_template","cloudformation_get_template_summary","cloudformation_list_stack_resources","cloudformation_update_stack","cloudformation_validate_template","cloudtrail_cancel_query","cloudtrail_describe_query","cloudtrail_describe_trails","cloudtrail_get_event_data_store","cloudtrail_get_event_selectors","cloudtrail_get_insight_selectors","cloudtrail_get_query_results","cloudtrail_get_trail","cloudtrail_get_trail_status","cloudtrail_list_event_data_stores","cloudtrail_list_tags","cloudtrail_list_trails","cloudtrail_lookup_events","cloudtrail_start_query","cloudwatch_describe_alarm_history","cloudwatch_describe_alarms","cloudwatch_describe_log_groups","cloudwatch_describe_log_streams","cloudwatch_filter_log_events","cloudwatch_get_log_events","cloudwatch_get_metric_statistics","cloudwatch_list_metrics","cloudwatch_mute_alarm","cloudwatch_put_log_group_retention","cloudwatch_put_metric_data","cloudwatch_query_logs","cloudwatch_unmute_alarm","coda_add_custom_domain","coda_add_permission","coda_change_user_role","coda_create_doc","coda_create_folder","coda_create_page","coda_delete_custom_domain","coda_delete_doc","coda_delete_folder","coda_delete_page","coda_delete_page_content","coda_delete_permission","coda_delete_row","coda_delete_rows","coda_export_page","coda_get_acl_settings","coda_get_analytics_last_updated","coda_get_column","coda_get_control","coda_get_custom_domain_provider","coda_get_doc","coda_get_doc_analytics_summary","coda_get_folder","coda_get_formula","coda_get_mutation_status","coda_get_page","coda_get_page_content","coda_get_page_export_status","coda_get_row","coda_get_sharing_metadata","coda_get_table","coda_list_categories","coda_list_columns","coda_list_controls","coda_list_custom_domains","coda_list_doc_analytics","coda_list_docs","coda_list_folder_children","coda_list_folders","coda_list_formulas","coda_list_page_analytics","coda_list_pages","coda_list_permissions","coda_list_rows","coda_list_tables","coda_list_workspace_members","coda_list_workspace_roles","coda_publish_doc","coda_push_button","coda_resolve_browser_link","coda_search_principals","coda_trigger_automation","coda_unpublish_doc","coda_update_acl_settings","coda_update_doc","coda_update_folder","coda_update_page","coda_update_row","coda_upsert_rows","coda_whoami","codepipeline_disable_stage_transition","codepipeline_enable_stage_transition","codepipeline_get_pipeline","codepipeline_get_pipeline_execution","codepipeline_get_pipeline_state","codepipeline_list_action_executions","codepipeline_list_pipeline_executions","codepipeline_list_pipelines","codepipeline_put_approval_result","codepipeline_retry_stage_execution","codepipeline_start_execution","codepipeline_stop_execution","confluence_add_label","confluence_create_blogpost","confluence_create_comment","confluence_create_page","confluence_create_page_property","confluence_create_space","confluence_create_space_property","confluence_delete_attachment","confluence_delete_blogpost","confluence_delete_comment","confluence_delete_label","confluence_delete_page","confluence_delete_page_property","confluence_delete_space","confluence_delete_space_property","confluence_get_blogpost","confluence_get_page_ancestors","confluence_get_page_children","confluence_get_page_descendants","confluence_get_page_version","confluence_get_pages_by_label","confluence_get_space","confluence_get_task","confluence_get_user","confluence_list_attachments","confluence_list_blogposts","confluence_list_blogposts_in_space","confluence_list_comments","confluence_list_labels","confluence_list_page_properties","confluence_list_page_versions","confluence_list_pages_in_space","confluence_list_space_labels","confluence_list_space_permissions","confluence_list_space_properties","confluence_list_spaces","confluence_list_tasks","confluence_retrieve","confluence_search","confluence_search_in_space","confluence_update","confluence_update_blogpost","confluence_update_comment","confluence_update_space","confluence_update_task","confluence_upload_attachment","context_dev_classify_naics","context_dev_classify_sic","context_dev_crawl","context_dev_extract","context_dev_extract_product","context_dev_extract_products","context_dev_get_brand","context_dev_get_brand_by_email","context_dev_get_brand_by_name","context_dev_get_brand_by_ticker","context_dev_identify_transaction","context_dev_map","context_dev_scrape_fonts","context_dev_scrape_html","context_dev_scrape_images","context_dev_scrape_markdown","context_dev_scrape_styleguide","context_dev_screenshot","context_dev_search","convex_action","convex_document_deltas","convex_list_documents","convex_list_tables","convex_mutation","convex_query","convex_run_function","crowdstrike_create_indicators","crowdstrike_delete_indicators","crowdstrike_delete_rtr_session","crowdstrike_execute_rtr_command","crowdstrike_get_alert_details","crowdstrike_get_case_details","crowdstrike_get_host_group_details","crowdstrike_get_indicator_details","crowdstrike_get_rtr_command_status","crowdstrike_get_sensor_aggregates","crowdstrike_get_sensor_details","crowdstrike_get_vulnerability_details","crowdstrike_init_rtr_session","crowdstrike_perform_host_action","crowdstrike_perform_host_group_action","crowdstrike_query_alerts","crowdstrike_query_cases","crowdstrike_query_host_groups","crowdstrike_query_indicators","crowdstrike_query_sensors","crowdstrike_query_vulnerabilities","crowdstrike_update_alerts","crowdstrike_update_indicators","crunchbase_autocomplete","crunchbase_get_acquisition","crunchbase_get_entity","crunchbase_get_entity_card","crunchbase_get_fields_metadata","crunchbase_get_funding_round","crunchbase_get_organization","crunchbase_get_person","crunchbase_list_deleted_entities","crunchbase_search_acquisitions","crunchbase_search_entities","crunchbase_search_funding_rounds","crunchbase_search_organizations","crunchbase_search_people","cursor_add_followup","cursor_add_followup_v2","cursor_delete_agent","cursor_delete_agent_v2","cursor_download_artifact","cursor_download_artifact_v2","cursor_get_agent","cursor_get_agent_v2","cursor_get_api_key_info","cursor_get_api_key_info_v2","cursor_get_conversation","cursor_get_conversation_v2","cursor_launch_agent","cursor_launch_agent_v2","cursor_list_agents","cursor_list_agents_v2","cursor_list_artifacts","cursor_list_artifacts_v2","cursor_list_models","cursor_list_models_v2","cursor_list_repositories","cursor_list_repositories_v2","cursor_stop_agent","cursor_stop_agent_v2","dagster_delete_run","dagster_get_asset","dagster_get_run","dagster_get_run_logs","dagster_launch_run","dagster_list_assets","dagster_list_jobs","dagster_list_runs","dagster_list_schedules","dagster_list_sensors","dagster_materialize_assets","dagster_reexecute_run","dagster_report_asset_materialization","dagster_start_schedule","dagster_start_sensor","dagster_stop_schedule","dagster_stop_sensor","dagster_terminate_run","dagster_wipe_asset","databricks_cancel_all_runs","databricks_cancel_run","databricks_cancel_statement","databricks_create_cluster","databricks_create_job","databricks_create_warehouse","databricks_delete_cluster","databricks_delete_job","databricks_delete_run","databricks_delete_warehouse","databricks_edit_cluster","databricks_edit_warehouse","databricks_execute_sql","databricks_genie_agent_ask","databricks_genie_agent_cancel","databricks_genie_agent_list_items","databricks_genie_ask","databricks_genie_create_space","databricks_genie_delete_conversation","databricks_genie_delete_message","databricks_genie_delete_space","databricks_genie_download_visualization","databricks_genie_execute_query","databricks_genie_get_message","databricks_genie_get_query_result","databricks_genie_get_space","databricks_genie_list_conversations","databricks_genie_list_messages","databricks_genie_list_spaces","databricks_genie_send_feedback","databricks_genie_update_space","databricks_get_cluster","databricks_get_job","databricks_get_run","databricks_get_run_output","databricks_get_statement","databricks_get_statement_chunk","databricks_get_warehouse","databricks_list_clusters","databricks_list_jobs","databricks_list_runs","databricks_list_warehouses","databricks_repair_run","databricks_reset_job","databricks_resize_cluster","databricks_restart_cluster","databricks_run_job","databricks_start_cluster","databricks_start_warehouse","databricks_stop_warehouse","databricks_submit_run","databricks_terminate_cluster","databricks_update_job","datadog_add_incident_todo","datadog_cancel_downtime","datadog_create_dashboard","datadog_create_downtime","datadog_create_event","datadog_create_incident","datadog_create_monitor","datadog_create_slo","datadog_delete_dashboard","datadog_delete_slo","datadog_get_browser_synthetics_results","datadog_get_dashboard","datadog_get_incident","datadog_get_monitor","datadog_get_security_signal","datadog_get_slo","datadog_get_slo_history","datadog_get_synthetics_results","datadog_get_synthetics_test","datadog_list_dashboards","datadog_list_downtimes","datadog_list_incidents","datadog_list_monitors","datadog_list_security_rules","datadog_list_security_signals","datadog_list_services","datadog_list_slos","datadog_list_synthetics_tests","datadog_mute_monitor","datadog_query_logs","datadog_query_timeseries","datadog_search_spans","datadog_send_logs","datadog_submit_metrics","datadog_trigger_synthetics_tests","datadog_unmute_monitor","datadog_update_incident","datadog_update_security_signal_assignee","datadog_update_security_signal_state","datadog_update_slo","datadog_update_synthetics_status","datagma_enrich_company","datagma_enrich_person","datagma_find_email","datagma_find_phone","datagma_get_credits","daytona_create_sandbox","daytona_delete_sandbox","daytona_download_file","daytona_execute_command","daytona_get_sandbox","daytona_git_clone","daytona_list_files","daytona_list_sandboxes","daytona_run_code","daytona_start_sandbox","daytona_stop_sandbox","daytona_upload_file","deployed_block_executor","deployments_deploy","deployments_get_version","deployments_list_versions","deployments_promote","deployments_undeploy","devin_append_session_tags","devin_archive_session","devin_create_session","devin_get_session","devin_get_session_tags","devin_list_session_attachments","devin_list_session_messages","devin_list_sessions","devin_replace_session_tags","devin_send_message","devin_terminate_session","discord_add_reaction","discord_archive_thread","discord_assign_role","discord_ban_member","discord_bulk_delete_messages","discord_create_channel","discord_create_invite","discord_create_role","discord_create_thread","discord_create_webhook","discord_delete_channel","discord_delete_invite","discord_delete_message","discord_delete_role","discord_delete_webhook","discord_edit_message","discord_execute_webhook","discord_get_channel","discord_get_invite","discord_get_member","discord_get_messages","discord_get_pinned_messages","discord_get_server","discord_get_user","discord_get_webhook","discord_join_thread","discord_kick_member","discord_leave_thread","discord_list_channels","discord_list_roles","discord_pin_message","discord_remove_reaction","discord_remove_role","discord_send_message","discord_unban_member","discord_unpin_message","discord_update_channel","discord_update_member","discord_update_role","docusign_create_from_template","docusign_download_document","docusign_get_envelope","docusign_list_envelopes","docusign_list_recipients","docusign_list_templates","docusign_send_envelope","docusign_void_envelope","downdetector_get_company","downdetector_get_company_attribution","downdetector_get_company_baseline","downdetector_get_company_events","downdetector_get_company_incidents","downdetector_get_company_indicators","downdetector_get_company_last_15","downdetector_get_company_status","downdetector_get_provider","downdetector_get_reports","downdetector_get_site_companies","downdetector_list_categories","downdetector_list_incidents","downdetector_list_sites","downdetector_search_companies","dropbox_copy","dropbox_create_folder","dropbox_create_shared_link","dropbox_delete","dropbox_download","dropbox_download_v2","dropbox_get_metadata","dropbox_list_folder","dropbox_list_revisions","dropbox_list_shared_links","dropbox_move","dropbox_restore","dropbox_search","dropbox_upload","dropcontact_enrich_contact","dspy_chain_of_thought","dspy_predict","dspy_react","dub_bulk_create_links","dub_bulk_delete_links","dub_bulk_update_links","dub_create_link","dub_create_tag","dub_delete_link","dub_get_analytics","dub_get_events","dub_get_link","dub_get_links_count","dub_get_qr_code","dub_get_qr_code_v2","dub_list_domains","dub_list_folders","dub_list_links","dub_list_tags","dub_update_link","dub_upsert_link","duckduckgo_search","dynamodb_delete","dynamodb_get","dynamodb_introspect","dynamodb_put","dynamodb_query","dynamodb_scan","dynamodb_update","dynatrace_add_problem_comment","dynatrace_add_tags","dynatrace_close_problem","dynatrace_create_settings_object","dynatrace_create_slo","dynatrace_delete_problem_comment","dynatrace_delete_settings_object","dynatrace_delete_slo","dynatrace_delete_tag","dynatrace_execute_synthetic_monitors","dynatrace_get_attack","dynatrace_get_audit_logs","dynatrace_get_entity","dynatrace_get_event","dynatrace_get_metric","dynatrace_get_problem","dynatrace_get_problem_comment","dynatrace_get_security_problem","dynatrace_get_settings_object","dynatrace_get_slo","dynatrace_get_synthetic_batch","dynatrace_ingest_event","dynatrace_ingest_logs","dynatrace_ingest_metrics","dynatrace_list_attacks","dynatrace_list_entities","dynatrace_list_entity_types","dynatrace_list_events","dynatrace_list_metrics","dynatrace_list_problem_comments","dynatrace_list_problems","dynatrace_list_remediation_items","dynatrace_list_security_problems","dynatrace_list_settings_objects","dynatrace_list_settings_schemas","dynatrace_list_slos","dynatrace_list_synthetic_monitors","dynatrace_list_tags","dynatrace_mute_security_problem","dynatrace_mute_security_problems","dynatrace_query_metrics","dynatrace_search_logs","dynatrace_unmute_security_problem","dynatrace_unmute_security_problems","dynatrace_update_problem_comment","dynatrace_update_settings_object","dynatrace_update_slo","ec2_allocate_address","ec2_associate_address","ec2_authorize_security_group_ingress","ec2_create_snapshot","ec2_create_tags","ec2_delete_snapshot","ec2_delete_tags","ec2_delete_volume","ec2_describe_addresses","ec2_describe_availability_zones","ec2_describe_images","ec2_describe_instance_status","ec2_describe_instances","ec2_describe_internet_gateways","ec2_describe_key_pairs","ec2_describe_nat_gateways","ec2_describe_network_acls","ec2_describe_network_interfaces","ec2_describe_regions","ec2_describe_route_tables","ec2_describe_security_groups","ec2_describe_snapshots","ec2_describe_subnets","ec2_describe_volumes","ec2_describe_vpc_endpoints","ec2_describe_vpc_peering_connections","ec2_describe_vpcs","ec2_disassociate_address","ec2_reboot_instances","ec2_release_address","ec2_revoke_security_group_ingress","ec2_start_instances","ec2_stop_instances","ec2_terminate_instances","elasticsearch_bulk","elasticsearch_cluster_health","elasticsearch_cluster_stats","elasticsearch_count","elasticsearch_create_index","elasticsearch_delete_document","elasticsearch_delete_index","elasticsearch_get_document","elasticsearch_get_index","elasticsearch_index_document","elasticsearch_list_indices","elasticsearch_search","elasticsearch_update_document","elevenlabs_audio_isolation","elevenlabs_edit_voice_settings","elevenlabs_get_user","elevenlabs_get_voice","elevenlabs_get_voice_settings","elevenlabs_list_models","elevenlabs_list_voices","elevenlabs_sound_effects","elevenlabs_speech_to_speech","elevenlabs_tts","emailbison_attach_leads_to_campaign","emailbison_attach_tags_to_leads","emailbison_create_campaign","emailbison_create_lead","emailbison_create_tag","emailbison_get_lead","emailbison_list_campaigns","emailbison_list_leads","emailbison_list_replies","emailbison_list_tags","emailbison_update_campaign","emailbison_update_campaign_status","emailbison_update_lead","embeddings_cohere","embeddings_gemini","embeddings_mistral","embeddings_ollama","embeddings_openai","embeddings_openrouter","enrich_check_credits","enrich_company_funding","enrich_company_lookup","enrich_company_revenue","enrich_disposable_email_check","enrich_email_to_ip","enrich_email_to_person_lite","enrich_email_to_phone","enrich_email_to_profile","enrich_find_email","enrich_get_post_details","enrich_ip_to_company","enrich_linkedin_profile","enrich_linkedin_to_personal_email","enrich_linkedin_to_work_email","enrich_phone_finder","enrich_reverse_hash_lookup","enrich_sales_pointer_people","enrich_search_company","enrich_search_company_activities","enrich_search_company_employees","enrich_search_jobs","enrich_search_logo","enrich_search_people","enrich_search_people_activities","enrich_search_post_comments","enrich_search_post_comments_by_url","enrich_search_post_reactions","enrich_search_post_reactions_by_url","enrich_search_posts","enrich_search_similar_companies","enrich_verify_email","enrichment_run","enrow_find_email","enrow_verify_email","exa_agent","exa_answer","exa_find_similar_links","exa_get_contents","exa_search","extend_parser","extend_parser_v2","fathom_get_summary","fathom_get_transcript","fathom_list_meeting_types","fathom_list_meetings","fathom_list_team_members","fathom_list_teams","file_append","file_compress","file_create_folder","file_decompress","file_delete_folder","file_edit","file_fetch","file_get","file_get_content","file_list","file_manage_sharing","file_move","file_parser","file_parser_v2","file_parser_v3","file_read","file_restore_folder","file_search","file_update_folder","file_write","findymail_find_email_from_linkedin","findymail_find_email_from_name","findymail_find_emails_by_domain","findymail_find_employees","findymail_find_phone","findymail_get_company","findymail_get_credits","findymail_lookup_technologies","findymail_reverse_email_lookup","findymail_search_technologies","findymail_verify_email","firecrawl_agent","firecrawl_batch_scrape","firecrawl_batch_scrape_status","firecrawl_cancel_crawl","firecrawl_crawl","firecrawl_crawl_status","firecrawl_credit_usage","firecrawl_enrich","firecrawl_extract","firecrawl_extract_status","firecrawl_map","firecrawl_parse","firecrawl_scrape","firecrawl_search","fireflies_add_to_live_meeting","fireflies_create_bite","fireflies_delete_transcript","fireflies_get_transcript","fireflies_get_user","fireflies_list_bites","fireflies_list_contacts","fireflies_list_transcripts","fireflies_list_users","fireflies_upload_audio","fivetran_cancel_transformation","fivetran_create_connect_card","fivetran_create_connection","fivetran_create_destination","fivetran_create_group","fivetran_delete_connection","fivetran_get_account_info","fivetran_get_connection","fivetran_get_connection_schema_config","fivetran_get_connection_warnings","fivetran_get_connector_type","fivetran_get_destination","fivetran_get_group","fivetran_get_sync_history","fivetran_get_transformation","fivetran_list_connections","fivetran_list_connector_types","fivetran_list_destinations","fivetran_list_groups","fivetran_list_transformations","fivetran_list_users","fivetran_reload_connection_schema","fivetran_resync_connection","fivetran_resync_tables","fivetran_run_connection_setup_tests","fivetran_run_destination_setup_tests","fivetran_run_transformation","fivetran_sync_connection","fivetran_update_connection","fivetran_update_connection_schema","fivetran_update_connection_table","fivetran_update_schema_config","flint_create_task","flint_generate_pages","flint_get_task","flow_add_link","flow_create_branch","flow_create_comment","flow_create_entities","flow_create_entity","flow_delete_comment","flow_delete_entity","flow_filter_entities","flow_get_comment","flow_get_data_model","flow_get_entities","flow_get_entity_history","flow_get_project","flow_list_branches","flow_list_comments","flow_list_entities","flow_list_projects","flow_remove_link","flow_restore_entity","flow_search_entities","flow_set_value","flow_set_values","flow_update_comment","function_execute","gamma_check_status","gamma_generate","gamma_generate_from_template","gamma_list_folders","gamma_list_themes","gauge_add_tags_to_prompts","gauge_bulk_update_prompts","gauge_create_prompts","gauge_create_tag","gauge_create_topic","gauge_delete_tag","gauge_get_answer","gauge_get_category","gauge_get_current_credential","gauge_get_organization","gauge_get_prompt","gauge_get_query_metadata","gauge_get_tag","gauge_get_topic","gauge_list_answers","gauge_list_brands","gauge_list_categories","gauge_list_citations","gauge_list_organizations","gauge_list_prompts","gauge_list_tags","gauge_list_topics","gauge_query","gauge_remove_tags_from_prompts","gauge_rename_topic","gauge_update_prompt","gauge_update_tag","github_add_assignees","github_add_assignees_v2","github_add_labels","github_add_labels_v2","github_cancel_workflow_run","github_cancel_workflow_run_v2","github_check_star","github_check_star_v2","github_close_issue","github_close_issue_v2","github_close_pr","github_close_pr_v2","github_comment","github_comment_v2","github_compare_commits","github_compare_commits_v2","github_create_branch","github_create_branch_v2","github_create_comment_reaction","github_create_comment_reaction_v2","github_create_file","github_create_file_v2","github_create_gist","github_create_gist_v2","github_create_issue","github_create_issue_reaction","github_create_issue_reaction_v2","github_create_issue_v2","github_create_milestone","github_create_milestone_v2","github_create_pr","github_create_pr_review","github_create_pr_review_v2","github_create_pr_v2","github_create_project","github_create_project_v2","github_create_release","github_create_release_v2","github_delete_branch","github_delete_branch_v2","github_delete_comment","github_delete_comment_reaction","github_delete_comment_reaction_v2","github_delete_comment_v2","github_delete_file","github_delete_file_v2","github_delete_gist","github_delete_gist_v2","github_delete_issue_reaction","github_delete_issue_reaction_v2","github_delete_milestone","github_delete_milestone_v2","github_delete_project","github_delete_project_v2","github_delete_release","github_delete_release_v2","github_fork_gist","github_fork_gist_v2","github_fork_repo","github_fork_repo_v2","github_get_branch","github_get_branch_protection","github_get_branch_protection_v2","github_get_branch_v2","github_get_commit","github_get_commit_v2","github_get_file_content","github_get_file_content_v2","github_get_gist","github_get_gist_v2","github_get_issue","github_get_issue_v2","github_get_latest_release","github_get_latest_release_v2","github_get_milestone","github_get_milestone_v2","github_get_pr_files","github_get_pr_files_v2","github_get_project","github_get_project_v2","github_get_readme","github_get_readme_v2","github_get_release","github_get_release_v2","github_get_tree","github_get_tree_v2","github_get_workflow","github_get_workflow_run","github_get_workflow_run_v2","github_get_workflow_v2","github_issue_comment","github_issue_comment_v2","github_job_logs","github_latest_commit","github_latest_commit_v2","github_list_branches","github_list_branches_v2","github_list_commits","github_list_commits_v2","github_list_forks","github_list_forks_v2","github_list_gists","github_list_gists_v2","github_list_issue_comments","github_list_issue_comments_v2","github_list_issues","github_list_issues_v2","github_list_milestones","github_list_milestones_v2","github_list_pr_comments","github_list_pr_comments_v2","github_list_projects","github_list_projects_v2","github_list_prs","github_list_prs_v2","github_list_releases","github_list_releases_v2","github_list_review_threads","github_list_stargazers","github_list_stargazers_v2","github_list_tags","github_list_tags_v2","github_list_workflow_runs","github_list_workflow_runs_v2","github_list_workflows","github_list_workflows_v2","github_merge_pr","github_merge_pr_v2","github_pr","github_pr_v2","github_remove_label","github_remove_label_v2","github_reply_review_thread","github_repo_info","github_repo_info_v2","github_request_reviewers","github_request_reviewers_v2","github_rerun_workflow","github_rerun_workflow_v2","github_resolve_review_thread","github_search_code","github_search_code_v2","github_search_commits","github_search_commits_v2","github_search_issues","github_search_issues_v2","github_search_repos","github_search_repos_v2","github_search_users","github_search_users_v2","github_star_gist","github_star_gist_v2","github_star_repo","github_star_repo_v2","github_status_check_rollup","github_trigger_workflow","github_trigger_workflow_v2","github_unstar_gist","github_unstar_gist_v2","github_unstar_repo","github_unstar_repo_v2","github_update_branch_protection","github_update_branch_protection_v2","github_update_comment","github_update_comment_v2","github_update_file","github_update_file_v2","github_update_gist","github_update_gist_v2","github_update_issue","github_update_issue_v2","github_update_milestone","github_update_milestone_v2","github_update_pr","github_update_pr_v2","github_update_project","github_update_project_v2","github_update_release","github_update_release_v2","gitlab_activate_user","gitlab_add_member","gitlab_add_saml_group_link","gitlab_approve_access_request","gitlab_approve_merge_request","gitlab_approve_user","gitlab_ban_user","gitlab_block_user","gitlab_cancel_pipeline","gitlab_compare_branches","gitlab_create_branch","gitlab_create_file","gitlab_create_issue","gitlab_create_issue_note","gitlab_create_merge_request","gitlab_create_merge_request_note","gitlab_create_pipeline","gitlab_create_release","gitlab_create_user","gitlab_deactivate_user","gitlab_delete_branch","gitlab_delete_issue","gitlab_delete_saml_group_link","gitlab_delete_user","gitlab_delete_user_identity","gitlab_deny_access_request","gitlab_get_file","gitlab_get_group","gitlab_get_issue","gitlab_get_job_log","gitlab_get_merge_request","gitlab_get_merge_request_changes","gitlab_get_pipeline","gitlab_get_project","gitlab_invite_member","gitlab_list_access_requests","gitlab_list_branches","gitlab_list_commits","gitlab_list_groups","gitlab_list_invitations","gitlab_list_issues","gitlab_list_members","gitlab_list_merge_requests","gitlab_list_pipeline_jobs","gitlab_list_pipelines","gitlab_list_projects","gitlab_list_releases","gitlab_list_repository_tree","gitlab_list_saml_group_links","gitlab_list_user_memberships","gitlab_merge_merge_request","gitlab_play_job","gitlab_reject_user","gitlab_remove_member","gitlab_retry_pipeline","gitlab_revoke_invitation","gitlab_search_users","gitlab_unban_user","gitlab_unblock_user","gitlab_update_file","gitlab_update_invitation","gitlab_update_issue","gitlab_update_member","gitlab_update_merge_request","gitlab_update_user","gmail_add_label","gmail_add_label_v2","gmail_archive","gmail_archive_v2","gmail_create_label_v2","gmail_delete","gmail_delete_draft_v2","gmail_delete_label_v2","gmail_delete_v2","gmail_draft","gmail_draft_v2","gmail_edit_draft_v2","gmail_get_draft_v2","gmail_get_thread_v2","gmail_list_drafts_v2","gmail_list_labels_v2","gmail_list_threads_v2","gmail_mark_read","gmail_mark_read_v2","gmail_mark_unread","gmail_mark_unread_v2","gmail_move","gmail_move_v2","gmail_read","gmail_read_v2","gmail_remove_label","gmail_remove_label_v2","gmail_search","gmail_search_v2","gmail_send","gmail_send_v2","gmail_trash_thread_v2","gmail_unarchive","gmail_unarchive_v2","gmail_untrash_thread_v2","gmail_update_label_v2","gong_aggregate_activity","gong_aggregate_by_period","gong_answered_scorecards","gong_ask_anything","gong_assign_flow_prospects","gong_create_call","gong_day_by_day_activity","gong_get_brief","gong_get_call","gong_get_call_transcript","gong_get_coaching","gong_get_extensive_calls","gong_get_folder_content","gong_get_logs","gong_get_prospect_flows","gong_get_user","gong_interaction_stats","gong_list_calls","gong_list_flows","gong_list_library_folders","gong_list_scorecards","gong_list_trackers","gong_list_users","gong_list_workspaces","gong_lookup_email","gong_lookup_phone","gong_purge_email_address","gong_purge_phone_number","gong_unassign_flow_prospects","google_ads_ad_performance","google_ads_campaign_performance","google_ads_list_ad_groups","google_ads_list_campaigns","google_ads_list_customers","google_ads_search","google_appsheet_add_rows","google_appsheet_delete_rows","google_appsheet_edit_rows","google_appsheet_find_rows","google_bigquery_create_dataset","google_bigquery_create_table","google_bigquery_delete_dataset","google_bigquery_delete_table","google_bigquery_get_query_results","google_bigquery_get_table","google_bigquery_insert_rows","google_bigquery_list_datasets","google_bigquery_list_table_data","google_bigquery_list_tables","google_bigquery_query","google_books_volume_details","google_books_volume_search","google_calendar_create","google_calendar_create_calendar","google_calendar_create_calendar_v2","google_calendar_create_v2","google_calendar_delete","google_calendar_delete_calendar","google_calendar_delete_calendar_v2","google_calendar_delete_v2","google_calendar_freebusy","google_calendar_freebusy_v2","google_calendar_get","google_calendar_get_v2","google_calendar_instances","google_calendar_instances_v2","google_calendar_invite","google_calendar_invite_v2","google_calendar_list","google_calendar_list_acl","google_calendar_list_acl_v2","google_calendar_list_calendars","google_calendar_list_calendars_v2","google_calendar_list_v2","google_calendar_move","google_calendar_move_v2","google_calendar_quick_add","google_calendar_quick_add_v2","google_calendar_respond","google_calendar_respond_v2","google_calendar_share_calendar","google_calendar_share_calendar_v2","google_calendar_unshare_calendar","google_calendar_unshare_calendar_v2","google_calendar_update","google_calendar_update_acl","google_calendar_update_acl_v2","google_calendar_update_calendar","google_calendar_update_calendar_v2","google_calendar_update_v2","google_contacts_create","google_contacts_delete","google_contacts_get","google_contacts_list","google_contacts_search","google_contacts_update","google_docs_create","google_docs_create_named_range","google_docs_create_paragraph_bullets","google_docs_delete_content_range","google_docs_delete_named_range","google_docs_delete_paragraph_bullets","google_docs_insert_image","google_docs_insert_page_break","google_docs_insert_table","google_docs_insert_text","google_docs_read","google_docs_replace_text","google_docs_update_paragraph_style","google_docs_update_text_style","google_docs_write","google_drive_copy","google_drive_create_comment","google_drive_create_folder","google_drive_delete","google_drive_delete_comment","google_drive_download","google_drive_export","google_drive_get_about","google_drive_get_content","google_drive_get_file","google_drive_get_revision","google_drive_list","google_drive_list_comments","google_drive_list_permissions","google_drive_list_revisions","google_drive_move","google_drive_search","google_drive_share","google_drive_trash","google_drive_unshare","google_drive_untrash","google_drive_update","google_drive_upload","google_forms_batch_update","google_forms_create_form","google_forms_create_watch","google_forms_delete_watch","google_forms_get_form","google_forms_get_responses","google_forms_list_watches","google_forms_renew_watch","google_forms_set_publish_settings","google_groups_add_alias","google_groups_add_member","google_groups_create_group","google_groups_delete_group","google_groups_get_group","google_groups_get_member","google_groups_get_settings","google_groups_has_member","google_groups_list_aliases","google_groups_list_groups","google_groups_list_members","google_groups_remove_alias","google_groups_remove_member","google_groups_update_group","google_groups_update_member","google_groups_update_settings","google_maps_air_quality","google_maps_directions","google_maps_distance_matrix","google_maps_elevation","google_maps_geocode","google_maps_geolocate","google_maps_place_details","google_maps_places_nearby","google_maps_places_search","google_maps_pollen","google_maps_reverse_geocode","google_maps_snap_to_roads","google_maps_solar","google_maps_speed_limits","google_maps_timezone","google_maps_validate_address","google_meet_create_space","google_meet_end_conference","google_meet_get_conference_record","google_meet_get_space","google_meet_list_conference_records","google_meet_list_participants","google_pagespeed_analyze","google_search","google_sheets_append","google_sheets_append_v2","google_sheets_batch_clear_v2","google_sheets_batch_get_v2","google_sheets_batch_update_v2","google_sheets_clear_v2","google_sheets_copy_sheet_v2","google_sheets_create_spreadsheet_v2","google_sheets_delete_rows_v2","google_sheets_delete_sheet_v2","google_sheets_delete_spreadsheet_v2","google_sheets_get_spreadsheet_v2","google_sheets_read","google_sheets_read_v2","google_sheets_update","google_sheets_update_v2","google_sheets_write","google_sheets_write_v2","google_slides_add_image","google_slides_add_slide","google_slides_batch_update","google_slides_copy_presentation","google_slides_create","google_slides_create_line","google_slides_create_paragraph_bullets","google_slides_create_shape","google_slides_create_sheets_chart","google_slides_create_table","google_slides_create_video","google_slides_delete_object","google_slides_delete_paragraph_bullets","google_slides_delete_table_column","google_slides_delete_table_row","google_slides_delete_text","google_slides_duplicate_object","google_slides_export_presentation","google_slides_get_page","google_slides_get_thumbnail","google_slides_group_objects","google_slides_insert_table_columns","google_slides_insert_table_rows","google_slides_insert_text","google_slides_merge_table_cells","google_slides_read","google_slides_refresh_sheets_chart","google_slides_replace_all_shapes_with_image","google_slides_replace_all_shapes_with_sheets_chart","google_slides_replace_all_text","google_slides_replace_image","google_slides_reroute_line","google_slides_ungroup_objects","google_slides_unmerge_table_cells","google_slides_update_image_properties","google_slides_update_line_category","google_slides_update_line_properties","google_slides_update_page_element_alt_text","google_slides_update_page_element_transform","google_slides_update_page_elements_z_order","google_slides_update_page_properties","google_slides_update_paragraph_style","google_slides_update_shape_properties","google_slides_update_slide_properties","google_slides_update_slides_position","google_slides_update_table_border_properties","google_slides_update_table_cell_properties","google_slides_update_table_column_properties","google_slides_update_table_row_properties","google_slides_update_text_style","google_slides_update_video_properties","google_slides_write","google_tasks_create","google_tasks_delete","google_tasks_get","google_tasks_list","google_tasks_list_task_lists","google_tasks_update","google_translate_detect","google_translate_text","google_vault_add_held_accounts","google_vault_add_matters_permissions","google_vault_close_matters","google_vault_create_matters","google_vault_create_matters_export","google_vault_create_matters_holds","google_vault_create_saved_query","google_vault_delete_matters","google_vault_delete_matters_export","google_vault_delete_matters_holds","google_vault_delete_saved_query","google_vault_download_export_file","google_vault_list_matters","google_vault_list_matters_export","google_vault_list_matters_holds","google_vault_list_saved_queries","google_vault_remove_held_accounts","google_vault_remove_matters_permissions","google_vault_reopen_matters","google_vault_undelete_matters","google_vault_update_matters","google_vault_update_matters_holds","grafana_check_data_source_health","grafana_create_alert_rule","grafana_create_annotation","grafana_create_contact_point","grafana_create_dashboard","grafana_create_folder","grafana_delete_alert_rule","grafana_delete_annotation","grafana_delete_contact_point","grafana_delete_dashboard","grafana_delete_folder","grafana_get_alert_rule","grafana_get_alert_rule_group","grafana_get_dashboard","grafana_get_data_source","grafana_get_folder","grafana_get_health","grafana_list_alert_rules","grafana_list_annotations","grafana_list_contact_points","grafana_list_dashboards","grafana_list_data_sources","grafana_list_folders","grafana_move_folder","grafana_query_data_source","grafana_update_alert_rule","grafana_update_annotation","grafana_update_contact_point","grafana_update_dashboard","grafana_update_folder","grain_create_hook","grain_create_hook_v2","grain_delete_hook","grain_delete_hook_v2","grain_get_recording","grain_get_transcript","grain_list_hooks","grain_list_hooks_v2","grain_list_meeting_types","grain_list_recordings","grain_list_teams","grain_list_views","granola_create_webhook_endpoint","granola_delete_webhook_endpoint","granola_get_note","granola_get_transcript","granola_list_audit_events","granola_list_folders","granola_list_notes","granola_list_webhook_endpoints","granola_update_webhook_endpoint","greenhouse_apply_candidate_tag_v2","greenhouse_convert_prospect_v2","greenhouse_create_application_v2","greenhouse_create_attachment_v2","greenhouse_create_candidate_tag_v2","greenhouse_create_candidate_v2","greenhouse_create_note_v2","greenhouse_delete_application_v2","greenhouse_delete_attachment_v2","greenhouse_delete_candidate_v2","greenhouse_get_application","greenhouse_get_application_v2","greenhouse_get_candidate","greenhouse_get_candidate_v2","greenhouse_get_job","greenhouse_get_job_v2","greenhouse_get_user","greenhouse_get_user_v2","greenhouse_hire_application_v2","greenhouse_list_application_stages_v2","greenhouse_list_applications","greenhouse_list_applications_v2","greenhouse_list_applied_candidate_tags_v2","greenhouse_list_attachments_v2","greenhouse_list_candidate_educations_v2","greenhouse_list_candidate_employments_v2","greenhouse_list_candidate_tags_v2","greenhouse_list_candidates","greenhouse_list_candidates_v2","greenhouse_list_close_reasons_v2","greenhouse_list_custom_field_options_v2","greenhouse_list_custom_fields_v2","greenhouse_list_departments","greenhouse_list_departments_v2","greenhouse_list_email_templates_v2","greenhouse_list_interviews_v2","greenhouse_list_job_hiring_managers_v2","greenhouse_list_job_interview_stages_v2","greenhouse_list_job_owners_v2","greenhouse_list_job_stages","greenhouse_list_jobs","greenhouse_list_jobs_v2","greenhouse_list_notes_v2","greenhouse_list_offers_v2","greenhouse_list_offices","greenhouse_list_offices_v2","greenhouse_list_openings_v2","greenhouse_list_prospect_pool_stages_v2","greenhouse_list_prospect_pools_v2","greenhouse_list_referrers_v2","greenhouse_list_rejection_reasons_v2","greenhouse_list_scorecards_v2","greenhouse_list_sources_v2","greenhouse_list_users","greenhouse_list_users_v2","greenhouse_move_application_v2","greenhouse_reject_application_v2","greenhouse_remove_candidate_tag_v2","greenhouse_unreject_application_v2","greenhouse_update_application_v2","greenhouse_update_candidate_v2","greptile_index_repo","greptile_query","greptile_search","greptile_status","guardrails_validate","harmonic_batch_get_people","harmonic_clear_people_saved_search_net_new_results","harmonic_enrich_person","harmonic_get_company_employees","harmonic_get_email_enrichment_job","harmonic_get_email_enrichment_usage","harmonic_get_enrichment_status","harmonic_get_people_saved_search_net_new_results","harmonic_get_people_saved_search_results","harmonic_get_person","harmonic_list_people_saved_searches","harmonic_search_people_scout","harmonic_submit_email_enrichment_job","helpscout_assign_conversation","helpscout_create_conversation","helpscout_create_customer","helpscout_create_customer_thread","helpscout_create_note","helpscout_create_reply","helpscout_delete_conversation","helpscout_get_conversation","helpscout_get_current_user","helpscout_get_customer","helpscout_get_organization","helpscout_get_saved_reply","helpscout_list_conversations","helpscout_list_custom_fields","helpscout_list_customer_properties","helpscout_list_customers","helpscout_list_folders","helpscout_list_mailboxes","helpscout_list_organizations","helpscout_list_saved_replies","helpscout_list_tags","helpscout_list_teams","helpscout_list_threads","helpscout_list_users","helpscout_list_workflows","helpscout_move_conversation","helpscout_run_workflow","helpscout_set_conversation_tags","helpscout_snooze_conversation","helpscout_unassign_conversation","helpscout_unsnooze_conversation","helpscout_update_conversation_customer","helpscout_update_conversation_status","helpscout_update_conversation_subject","helpscout_update_custom_fields","helpscout_update_customer","helpscout_update_customer_properties","hex_cancel_run","hex_create_collection","hex_create_group","hex_deactivate_user","hex_delete_group","hex_get_collection","hex_get_data_connection","hex_get_group","hex_get_project","hex_get_project_runs","hex_get_queried_tables","hex_get_run_status","hex_list_collections","hex_list_data_connections","hex_list_groups","hex_list_projects","hex_list_users","hex_run_project","hex_update_collection","hex_update_group","hex_update_project","higgsfield_cancel_request","higgsfield_create_character","higgsfield_delete_character","higgsfield_delete_request","higgsfield_generate","higgsfield_generate_soul_image","higgsfield_get_character","higgsfield_get_request_status","higgsfield_list_characters","http_request","hubspot_add_list_memberships","hubspot_create_appointment","hubspot_create_association","hubspot_create_company","hubspot_create_contact","hubspot_create_deal","hubspot_create_email","hubspot_create_line_item","hubspot_create_list","hubspot_create_note","hubspot_create_ticket","hubspot_delete_association","hubspot_delete_company","hubspot_delete_contact","hubspot_delete_deal","hubspot_delete_line_item","hubspot_delete_ticket","hubspot_get_appointment","hubspot_get_association_labels","hubspot_get_cart","hubspot_get_company","hubspot_get_contact","hubspot_get_deal","hubspot_get_email","hubspot_get_line_item","hubspot_get_list","hubspot_get_list_memberships","hubspot_get_marketing_event","hubspot_get_note","hubspot_get_properties","hubspot_get_quote","hubspot_get_ticket","hubspot_get_users","hubspot_list_appointments","hubspot_list_associations","hubspot_list_carts","hubspot_list_companies","hubspot_list_contacts","hubspot_list_deals","hubspot_list_emails","hubspot_list_line_items","hubspot_list_lists","hubspot_list_marketing_events","hubspot_list_notes","hubspot_list_owners","hubspot_list_quotes","hubspot_list_tickets","hubspot_remove_list_memberships","hubspot_search_companies","hubspot_search_contacts","hubspot_search_deals","hubspot_search_emails","hubspot_search_line_items","hubspot_search_notes","hubspot_search_quotes","hubspot_search_tickets","hubspot_update_appointment","hubspot_update_company","hubspot_update_contact","hubspot_update_deal","hubspot_update_line_item","hubspot_update_ticket","huggingface_chat","hunter_companies_find","hunter_discover","hunter_domain_search","hunter_email_count","hunter_email_finder","hunter_email_verifier","iam_add_user_to_group","iam_attach_role_policy","iam_attach_user_policy","iam_create_access_key","iam_create_role","iam_create_user","iam_delete_access_key","iam_delete_role","iam_delete_user","iam_detach_role_policy","iam_detach_user_policy","iam_get_policy","iam_get_role","iam_get_user","iam_list_access_keys","iam_list_attached_role_policies","iam_list_attached_user_policies","iam_list_groups","iam_list_policies","iam_list_roles","iam_list_users","iam_remove_user_from_group","iam_simulate_principal_policy","iam_update_access_key","icypeas_find_email","icypeas_verify_email","identity_center_check_assignment_deletion_status","identity_center_check_assignment_status","identity_center_create_account_assignment","identity_center_delete_account_assignment","identity_center_describe_account","identity_center_describe_group","identity_center_describe_user","identity_center_get_group","identity_center_get_user","identity_center_list_account_assignments","identity_center_list_accounts","identity_center_list_assignments_for_account","identity_center_list_group_memberships","identity_center_list_groups","identity_center_list_instances","identity_center_list_permission_sets","image_generate","incidentio_actions_create","incidentio_actions_list","incidentio_actions_show","incidentio_actions_update","incidentio_alert_events_create","incidentio_alerts_list","incidentio_alerts_resolve","incidentio_alerts_show","incidentio_catalog_entries_list","incidentio_catalog_types_list","incidentio_custom_fields_create","incidentio_custom_fields_delete","incidentio_custom_fields_list","incidentio_custom_fields_show","incidentio_custom_fields_update","incidentio_escalation_paths_create","incidentio_escalation_paths_delete","incidentio_escalation_paths_list","incidentio_escalation_paths_show","incidentio_escalation_paths_update","incidentio_escalations_cancel","incidentio_escalations_create","incidentio_escalations_list","incidentio_escalations_show","incidentio_follow_ups_create","incidentio_follow_ups_list","incidentio_follow_ups_show","incidentio_follow_ups_update","incidentio_incident_alerts_list","incidentio_incident_memberships_create","incidentio_incident_memberships_revoke","incidentio_incident_participants_list","incidentio_incident_roles_create","incidentio_incident_roles_delete","incidentio_incident_roles_list","incidentio_incident_roles_show","incidentio_incident_roles_update","incidentio_incident_statuses_list","incidentio_incident_timestamps_list","incidentio_incident_timestamps_show","incidentio_incident_types_list","incidentio_incident_updates_list","incidentio_incidents_create","incidentio_incidents_list","incidentio_incidents_show","incidentio_incidents_update","incidentio_on_call_now","incidentio_schedule_entries_list","incidentio_schedule_overrides_create","incidentio_schedule_overrides_list","incidentio_schedules_create","incidentio_schedules_delete","incidentio_schedules_list","incidentio_schedules_show","incidentio_schedules_update","incidentio_severities_list","incidentio_teams_list","incidentio_teams_show","incidentio_users_list","incidentio_users_show","incidentio_workflows_create","incidentio_workflows_delete","incidentio_workflows_list","incidentio_workflows_show","incidentio_workflows_update","infisical_create_secret","infisical_delete_secret","infisical_get_secret","infisical_list_secrets","infisical_update_secret","instagram_delete_comment","instagram_download_media","instagram_get_account_insights","instagram_get_container_status","instagram_get_conversation_messages","instagram_get_media","instagram_get_media_insights","instagram_get_message","instagram_get_profile","instagram_get_publishing_limit","instagram_hide_comment","instagram_list_comments","instagram_list_conversations","instagram_list_media","instagram_list_stories","instagram_private_reply","instagram_publish_carousel","instagram_publish_image","instagram_publish_reel","instagram_publish_story","instagram_publish_video","instagram_reply_to_comment","instagram_send_text_message","instagram_set_comments_enabled","instantly_activate_campaign","instantly_create_campaign","instantly_create_lead","instantly_create_lead_list","instantly_delete_campaign","instantly_delete_leads","instantly_get_lead","instantly_list_campaigns","instantly_list_emails","instantly_list_lead_lists","instantly_list_leads","instantly_patch_campaign","instantly_patch_lead","instantly_pause_campaign","instantly_reply_to_email","instantly_update_lead_interest_status","intercom_assign_conversation_v2","intercom_attach_contact_to_company_v2","intercom_close_conversation_v2","intercom_create_company","intercom_create_company_v2","intercom_create_contact","intercom_create_contact_v2","intercom_create_event_v2","intercom_create_message","intercom_create_message_v2","intercom_create_note_v2","intercom_create_tag_v2","intercom_create_ticket","intercom_create_ticket_v2","intercom_delete_contact","intercom_delete_contact_v2","intercom_detach_contact_from_company_v2","intercom_get_company","intercom_get_company_v2","intercom_get_contact","intercom_get_contact_v2","intercom_get_conversation","intercom_get_conversation_v2","intercom_get_ticket","intercom_get_ticket_v2","intercom_list_admins_v2","intercom_list_companies","intercom_list_companies_v2","intercom_list_contacts","intercom_list_contacts_v2","intercom_list_conversations","intercom_list_conversations_v2","intercom_list_tags_v2","intercom_open_conversation_v2","intercom_reply_conversation","intercom_reply_conversation_v2","intercom_search_contacts","intercom_search_contacts_v2","intercom_search_conversations","intercom_search_conversations_v2","intercom_snooze_conversation_v2","intercom_tag_contact_v2","intercom_tag_conversation_v2","intercom_untag_contact_v2","intercom_update_contact","intercom_update_contact_v2","intercom_update_ticket_v2","intune_get_compliance_policy","intune_get_detected_app","intune_get_device","intune_get_device_configuration","intune_list_app_devices","intune_list_compliance_policies","intune_list_compliance_policy_device_statuses","intune_list_detected_apps","intune_list_device_configurations","intune_list_devices","intune_reboot_device","intune_remote_lock_device","intune_retire_device","intune_sync_device","jama_create_comment","jama_create_item","jama_create_relationship","jama_delete_item","jama_delete_relationship","jama_get_baseline","jama_get_current_user","jama_get_filter","jama_get_item","jama_get_item_type","jama_get_item_version","jama_get_project","jama_get_relationship","jama_get_test_cycle","jama_get_test_plan","jama_get_test_run","jama_list_baseline_items","jama_list_baselines","jama_list_downstream_items","jama_list_filter_results","jama_list_filters","jama_list_item_children","jama_list_item_comments","jama_list_item_types","jama_list_item_versions","jama_list_items","jama_list_pick_list_options","jama_list_pick_lists","jama_list_projects","jama_list_relationship_types","jama_list_relationships","jama_list_releases","jama_list_tags","jama_list_test_cycles","jama_list_test_plans","jama_list_test_runs","jama_list_upstream_items","jama_search_items","jama_update_item","jama_update_test_run","jina_read_url","jina_search","jira_add_attachment","jira_add_comment","jira_add_watcher","jira_add_worklog","jira_assign_issue","jira_bulk_read","jira_create_issue_link","jira_create_sprint","jira_delete_attachment","jira_delete_comment","jira_delete_issue","jira_delete_issue_link","jira_delete_worklog","jira_get_attachments","jira_get_comments","jira_get_fields","jira_get_project","jira_get_transitions","jira_get_users","jira_get_worklogs","jira_list_issue_types","jira_list_projects","jira_remove_watcher","jira_retrieve","jira_search_issues","jira_search_users","jira_transition_issue","jira_update","jira_update_comment","jira_update_worklog","jira_write","jotform_add_label_resources","jotform_clone_form","jotform_create_form","jotform_create_label","jotform_create_question","jotform_create_questions","jotform_create_report","jotform_create_submission","jotform_create_submissions","jotform_create_webhook","jotform_delete_form","jotform_delete_label","jotform_delete_question","jotform_delete_report","jotform_delete_submission","jotform_delete_webhook","jotform_get_form","jotform_get_form_properties","jotform_get_history","jotform_get_label","jotform_get_question","jotform_get_report","jotform_get_settings","jotform_get_submission","jotform_get_usage","jotform_get_user","jotform_list_form_files","jotform_list_form_reports","jotform_list_form_submissions","jotform_list_forms","jotform_list_label_resources","jotform_list_labels","jotform_list_questions","jotform_list_reports","jotform_list_submissions","jotform_list_subusers","jotform_list_webhooks","jotform_remove_label_resources","jotform_update_form_properties","jotform_update_label","jotform_update_question","jotform_update_settings","jotform_update_submission","jsm_add_comment","jsm_add_customer","jsm_add_organization","jsm_add_participants","jsm_answer_approval","jsm_attach_form","jsm_copy_forms","jsm_create_object","jsm_create_organization","jsm_create_request","jsm_delete_form","jsm_delete_object","jsm_externalise_form","jsm_get_approvals","jsm_get_comments","jsm_get_customers","jsm_get_form","jsm_get_form_answers","jsm_get_form_structure","jsm_get_form_templates","jsm_get_issue_forms","jsm_get_object","jsm_get_object_schema","jsm_get_object_type_attributes","jsm_get_organizations","jsm_get_participants","jsm_get_queues","jsm_get_request","jsm_get_request_type_fields","jsm_get_request_types","jsm_get_requests","jsm_get_service_desks","jsm_get_sla","jsm_get_transitions","jsm_internalise_form","jsm_list_object_schemas","jsm_list_object_types","jsm_reopen_form","jsm_save_form_answers","jsm_search_objects_aql","jsm_submit_form","jsm_transition_request","jsm_update_object","jupyter_copy_content","jupyter_create_file","jupyter_create_session","jupyter_delete_content","jupyter_delete_session","jupyter_get_content","jupyter_get_content_v2","jupyter_interrupt_kernel","jupyter_list_contents","jupyter_list_kernels","jupyter_list_kernelspecs","jupyter_list_sessions","jupyter_rename_content","jupyter_restart_kernel","jupyter_start_kernel","jupyter_stop_kernel","jupyter_upload_file","kalshi_amend_order","kalshi_amend_order_v2","kalshi_cancel_order","kalshi_cancel_order_v2","kalshi_create_order","kalshi_create_order_v2","kalshi_get_balance","kalshi_get_balance_v2","kalshi_get_candlesticks","kalshi_get_candlesticks_v2","kalshi_get_event","kalshi_get_event_candlesticks","kalshi_get_event_candlesticks_v2","kalshi_get_event_v2","kalshi_get_events","kalshi_get_events_v2","kalshi_get_exchange_announcements","kalshi_get_exchange_announcements_v2","kalshi_get_exchange_schedule","kalshi_get_exchange_schedule_v2","kalshi_get_exchange_status","kalshi_get_exchange_status_v2","kalshi_get_fills","kalshi_get_fills_v2","kalshi_get_market","kalshi_get_market_v2","kalshi_get_markets","kalshi_get_markets_v2","kalshi_get_order","kalshi_get_order_v2","kalshi_get_orderbook","kalshi_get_orderbook_v2","kalshi_get_orders","kalshi_get_orders_v2","kalshi_get_positions","kalshi_get_positions_v2","kalshi_get_series_by_ticker","kalshi_get_series_by_ticker_v2","kalshi_get_series_list","kalshi_get_series_list_v2","kalshi_get_settlements","kalshi_get_settlements_v2","kalshi_get_trades","kalshi_get_trades_v2","ketch_get_consent","ketch_get_subscriptions","ketch_invoke_right","ketch_set_consent","ketch_set_subscriptions","knowledge_create_document","knowledge_delete_chunk","knowledge_delete_document","knowledge_get_connector","knowledge_get_document","knowledge_list_chunks","knowledge_list_connectors","knowledge_list_documents","knowledge_list_tags","knowledge_search","knowledge_trigger_sync","knowledge_update_chunk","knowledge_upload_chunk","knowledge_upsert_document","lambda_add_permission","lambda_create_alias","lambda_create_event_source_mapping","lambda_create_function","lambda_create_function_url_config","lambda_delete_alias","lambda_delete_event_source_mapping","lambda_delete_function","lambda_delete_function_concurrency","lambda_delete_function_event_invoke_config","lambda_delete_function_url_config","lambda_delete_provisioned_concurrency_config","lambda_get_account_settings","lambda_get_alias","lambda_get_event_source_mapping","lambda_get_function","lambda_get_function_concurrency","lambda_get_function_configuration","lambda_get_function_event_invoke_config","lambda_get_function_recursion_config","lambda_get_function_url_config","lambda_get_layer_version","lambda_get_policy","lambda_get_provisioned_concurrency_config","lambda_get_runtime_management_config","lambda_invoke","lambda_list_aliases","lambda_list_event_source_mappings","lambda_list_function_event_invoke_configs","lambda_list_function_url_configs","lambda_list_functions","lambda_list_layer_versions","lambda_list_layers","lambda_list_provisioned_concurrency_configs","lambda_list_tags","lambda_list_versions_by_function","lambda_publish_version","lambda_put_function_concurrency","lambda_put_function_event_invoke_config","lambda_put_function_recursion_config","lambda_put_provisioned_concurrency_config","lambda_put_runtime_management_config","lambda_remove_permission","lambda_tag_resource","lambda_untag_resource","lambda_update_alias","lambda_update_event_source_mapping","lambda_update_function_code","lambda_update_function_configuration","lambda_update_function_url_config","langsmith_create_feedback","langsmith_create_run","langsmith_create_runs_batch","langsmith_get_run","langsmith_update_run","latex_compile","latex_get_package","latex_list_fonts","latex_search_packages","launchdarkly_create_flag","launchdarkly_delete_flag","launchdarkly_get_audit_log","launchdarkly_get_flag","launchdarkly_get_flag_status","launchdarkly_list_environments","launchdarkly_list_flags","launchdarkly_list_members","launchdarkly_list_projects","launchdarkly_list_segments","launchdarkly_toggle_flag","launchdarkly_update_flag","leadmagic_company_search","leadmagic_email_to_profile","leadmagic_find_email","leadmagic_find_mobile","leadmagic_get_credits","leadmagic_profile_search","leadmagic_profile_to_email","leadmagic_role_finder","leadmagic_validate_email","lemlist_get_activities","lemlist_get_lead","lemlist_send_email","linear_add_label_to_issue","linear_add_label_to_project","linear_archive_issue","linear_archive_label","linear_archive_project","linear_create_attachment","linear_create_comment","linear_create_customer","linear_create_customer_request","linear_create_customer_status","linear_create_customer_tier","linear_create_cycle","linear_create_favorite","linear_create_issue","linear_create_issue_relation","linear_create_label","linear_create_project","linear_create_project_label","linear_create_project_milestone","linear_create_project_status","linear_create_project_update","linear_create_workflow_state","linear_delete_attachment","linear_delete_comment","linear_delete_customer","linear_delete_customer_status","linear_delete_customer_tier","linear_delete_issue","linear_delete_issue_relation","linear_delete_project","linear_delete_project_label","linear_delete_project_milestone","linear_delete_project_status","linear_get_active_cycle","linear_get_customer","linear_get_cycle","linear_get_issue","linear_get_project","linear_get_viewer","linear_list_attachments","linear_list_comments","linear_list_customer_requests","linear_list_customer_statuses","linear_list_customer_tiers","linear_list_customers","linear_list_cycles","linear_list_favorites","linear_list_issue_relations","linear_list_labels","linear_list_notifications","linear_list_project_labels","linear_list_project_milestones","linear_list_project_statuses","linear_list_project_updates","linear_list_projects","linear_list_teams","linear_list_users","linear_list_workflow_states","linear_merge_customers","linear_read_issues","linear_remove_label_from_issue","linear_remove_label_from_project","linear_search_issues","linear_unarchive_issue","linear_update_attachment","linear_update_comment","linear_update_customer","linear_update_customer_request","linear_update_customer_status","linear_update_customer_tier","linear_update_issue","linear_update_label","linear_update_notification","linear_update_project","linear_update_project_label","linear_update_project_milestone","linear_update_project_status","linear_update_workflow_state","linkedin_get_profile","linkedin_share_post","linkup_search","linq_add_participant","linq_check_imessage","linq_check_rcs","linq_create_attachment","linq_create_chat","linq_create_contact_card","linq_create_webhook_subscription","linq_delete_attachment","linq_delete_message","linq_delete_webhook_subscription","linq_edit_message","linq_get_attachment","linq_get_chat","linq_get_contact_card","linq_get_message","linq_get_webhook_subscription","linq_leave_chat","linq_list_chats","linq_list_messages","linq_list_phone_numbers","linq_list_thread","linq_list_webhook_events","linq_list_webhook_subscriptions","linq_mark_chat_read","linq_react_to_message","linq_remove_participant","linq_send_message","linq_send_voice_memo","linq_share_contact_card","linq_start_typing","linq_stop_typing","linq_update_chat","linq_update_contact_card","linq_update_webhook_subscription","llm_chat","logfire_get_token_info","logfire_get_trace","logfire_query","logfire_search_records","logrocket_create_release","logrocket_get_audit_logs","logrocket_get_highlights","logrocket_identify_user","logrocket_list_exported_sessions","logrocket_request_highlights","logs_get","logs_get_execution","logs_get_run_details","logs_query","logs_query_runs","loops_check_contact_suppression","loops_create_contact","loops_create_contact_property","loops_delete_contact","loops_find_contact","loops_get_transactional_email","loops_list_contact_properties","loops_list_mailing_lists","loops_list_transactional_emails","loops_remove_contact_suppression","loops_send_event","loops_send_transactional_email","loops_update_contact","luma_add_guests","luma_cancel_event","luma_create_event","luma_get_event","luma_get_guest","luma_get_guests","luma_list_events","luma_lookup_event","luma_send_invites","luma_update_event","luma_update_guest_status","mailchimp_add_member","mailchimp_add_member_tags","mailchimp_add_or_update_member","mailchimp_add_segment_member","mailchimp_add_subscriber_to_automation","mailchimp_archive_member","mailchimp_create_audience","mailchimp_create_batch_operation","mailchimp_create_campaign","mailchimp_create_interest","mailchimp_create_interest_category","mailchimp_create_landing_page","mailchimp_create_merge_field","mailchimp_create_segment","mailchimp_create_template","mailchimp_delete_audience","mailchimp_delete_batch_operation","mailchimp_delete_campaign","mailchimp_delete_interest","mailchimp_delete_interest_category","mailchimp_delete_landing_page","mailchimp_delete_member","mailchimp_delete_member_permanent","mailchimp_delete_merge_field","mailchimp_delete_segment","mailchimp_delete_template","mailchimp_get_audience","mailchimp_get_audiences","mailchimp_get_automation","mailchimp_get_automations","mailchimp_get_batch_operation","mailchimp_get_batch_operations","mailchimp_get_campaign","mailchimp_get_campaign_content","mailchimp_get_campaign_report","mailchimp_get_campaign_reports","mailchimp_get_campaigns","mailchimp_get_interest","mailchimp_get_interest_categories","mailchimp_get_interest_category","mailchimp_get_interests","mailchimp_get_landing_page","mailchimp_get_landing_pages","mailchimp_get_member","mailchimp_get_member_tags","mailchimp_get_members","mailchimp_get_merge_field","mailchimp_get_merge_fields","mailchimp_get_segment","mailchimp_get_segment_members","mailchimp_get_segments","mailchimp_get_template","mailchimp_get_templates","mailchimp_pause_automation","mailchimp_publish_landing_page","mailchimp_remove_member_tags","mailchimp_remove_segment_member","mailchimp_replicate_campaign","mailchimp_schedule_campaign","mailchimp_send_campaign","mailchimp_set_campaign_content","mailchimp_start_automation","mailchimp_unarchive_member","mailchimp_unpublish_landing_page","mailchimp_unschedule_campaign","mailchimp_update_audience","mailchimp_update_campaign","mailchimp_update_interest","mailchimp_update_interest_category","mailchimp_update_landing_page","mailchimp_update_member","mailchimp_update_merge_field","mailchimp_update_segment","mailchimp_update_template","mailgun_add_list_member","mailgun_create_mailing_list","mailgun_get_domain","mailgun_get_mailing_list","mailgun_get_message","mailgun_list_domains","mailgun_list_messages","mailgun_send_message","managed_agent_archive_session","managed_agent_create_session","managed_agent_delete_session","managed_agent_get_session","managed_agent_interrupt_session","managed_agent_list_events","managed_agent_respond_custom_tool","managed_agent_respond_tool_confirmation","managed_agent_run_session","managed_agent_send_message","managed_agent_update_session","manageengine_sdp_add_change_note","manageengine_sdp_add_problem_note","manageengine_sdp_add_request_note","manageengine_sdp_create_asset","manageengine_sdp_create_change","manageengine_sdp_create_problem","manageengine_sdp_create_request","manageengine_sdp_create_solution","manageengine_sdp_delete_asset","manageengine_sdp_delete_change","manageengine_sdp_delete_problem","manageengine_sdp_delete_request","manageengine_sdp_delete_solution","manageengine_sdp_get_asset","manageengine_sdp_get_change","manageengine_sdp_get_problem","manageengine_sdp_get_request","manageengine_sdp_get_solution","manageengine_sdp_list_assets","manageengine_sdp_list_change_notes","manageengine_sdp_list_changes","manageengine_sdp_list_problem_notes","manageengine_sdp_list_problems","manageengine_sdp_list_request_notes","manageengine_sdp_list_requests","manageengine_sdp_list_solutions","manageengine_sdp_update_asset","manageengine_sdp_update_change","manageengine_sdp_update_problem","manageengine_sdp_update_request","manageengine_sdp_update_solution","mcp_list_operations","mcp_run_operation","mem0_add_memories","mem0_get_memories","mem0_search_memories","memory_add","memory_delete","memory_get","memory_get_all","microsoft_ad_add_directory_role_member","microsoft_ad_add_group_member","microsoft_ad_add_user_app_role_assignment","microsoft_ad_assign_license","microsoft_ad_create_group","microsoft_ad_create_user","microsoft_ad_delete_group","microsoft_ad_delete_user","microsoft_ad_get_conditional_access_policy","microsoft_ad_get_device","microsoft_ad_get_group","microsoft_ad_get_user","microsoft_ad_list_authentication_methods","microsoft_ad_list_conditional_access_policies","microsoft_ad_list_devices","microsoft_ad_list_directory_audits","microsoft_ad_list_directory_role_members","microsoft_ad_list_directory_roles","microsoft_ad_list_group_members","microsoft_ad_list_groups","microsoft_ad_list_service_principal_app_role_assignments","microsoft_ad_list_service_principals","microsoft_ad_list_sign_ins","microsoft_ad_list_subscribed_skus","microsoft_ad_list_user_app_role_assignments","microsoft_ad_list_user_devices","microsoft_ad_list_user_licenses","microsoft_ad_list_user_memberships","microsoft_ad_list_users","microsoft_ad_remove_directory_role_member","microsoft_ad_remove_group_member","microsoft_ad_remove_user_app_role_assignment","microsoft_ad_reset_password","microsoft_ad_revoke_sign_in_sessions","microsoft_ad_set_password","microsoft_ad_update_group","microsoft_ad_update_user","microsoft_dataverse_associate","microsoft_dataverse_create_multiple","microsoft_dataverse_create_record","microsoft_dataverse_delete_record","microsoft_dataverse_disassociate","microsoft_dataverse_download_file","microsoft_dataverse_download_file_v2","microsoft_dataverse_execute_action","microsoft_dataverse_execute_function","microsoft_dataverse_fetchxml_query","microsoft_dataverse_get_entity_metadata","microsoft_dataverse_get_record","microsoft_dataverse_list_records","microsoft_dataverse_search","microsoft_dataverse_update_multiple","microsoft_dataverse_update_record","microsoft_dataverse_upload_file","microsoft_dataverse_upsert_record","microsoft_dataverse_whoami","microsoft_dynamics_365_close_case","microsoft_dynamics_365_close_opportunity","microsoft_dynamics_365_create_record","microsoft_dynamics_365_get_record","microsoft_dynamics_365_list_records","microsoft_dynamics_365_qualify_lead","microsoft_dynamics_365_search_records","microsoft_dynamics_365_update_record","microsoft_excel_calculate","microsoft_excel_clear_range","microsoft_excel_close_session","microsoft_excel_create_session","microsoft_excel_create_table","microsoft_excel_delete_table","microsoft_excel_delete_table_row","microsoft_excel_delete_worksheet","microsoft_excel_format_range","microsoft_excel_get_table","microsoft_excel_get_table_row","microsoft_excel_get_worksheet","microsoft_excel_list_table_rows","microsoft_excel_list_tables","microsoft_excel_list_worksheets","microsoft_excel_read","microsoft_excel_read_v2","microsoft_excel_refresh_session","microsoft_excel_sort_range","microsoft_excel_table_add","microsoft_excel_update_table","microsoft_excel_update_table_row","microsoft_excel_update_worksheet","microsoft_excel_worksheet_add","microsoft_excel_write","microsoft_excel_write_v2","microsoft_planner_create_bucket","microsoft_planner_create_plan","microsoft_planner_create_task","microsoft_planner_delete_bucket","microsoft_planner_delete_plan","microsoft_planner_delete_task","microsoft_planner_get_plan_details","microsoft_planner_get_task_details","microsoft_planner_list_buckets","microsoft_planner_list_plans","microsoft_planner_read_bucket","microsoft_planner_read_plan","microsoft_planner_read_task","microsoft_planner_update_bucket","microsoft_planner_update_plan","microsoft_planner_update_plan_details","microsoft_planner_update_task","microsoft_planner_update_task_details","microsoft_teams_delete_channel_message","microsoft_teams_delete_chat_message","microsoft_teams_get_message","microsoft_teams_list_channel_members","microsoft_teams_list_channels","microsoft_teams_list_chat_members","microsoft_teams_list_chats","microsoft_teams_list_team_members","microsoft_teams_list_teams","microsoft_teams_read_channel","microsoft_teams_read_chat","microsoft_teams_reply_to_message","microsoft_teams_set_reaction","microsoft_teams_unset_reaction","microsoft_teams_update_channel_message","microsoft_teams_update_chat_message","microsoft_teams_write_channel","microsoft_teams_write_chat","microsoft_word_append","microsoft_word_create","microsoft_word_create_from_template","microsoft_word_export_pdf","microsoft_word_list","microsoft_word_read","microsoft_word_replace_text","microsoft_word_update","millionverifier_get_credits","millionverifier_verify_email","mintlify_create_agent_job","mintlify_create_assistant_message","mintlify_detect_ai_prose","mintlify_get_agent_job","mintlify_get_assistant_caller_stats","mintlify_get_assistant_conversations","mintlify_get_feedback","mintlify_get_feedback_by_page","mintlify_get_page_content","mintlify_get_searches","mintlify_get_update_status","mintlify_get_views","mintlify_get_visitors","mintlify_search","mintlify_send_agent_message","mintlify_trigger_automation","mintlify_trigger_preview","mintlify_trigger_update","mistral_parser","mistral_parser_v2","mistral_parser_v3","modal_call_function","modal_chat_completion","modal_list_models","monaco_add_audience_contacts","monaco_add_campaign_audiences","monaco_create_audience","monaco_create_contact","monaco_create_opportunity","monaco_create_tag","monaco_create_task","monaco_delete_account","monaco_delete_contact","monaco_delete_opportunity","monaco_enroll_campaign_contacts","monaco_get_account","monaco_get_audience","monaco_get_campaign","monaco_get_contact","monaco_get_conversation","monaco_get_current_user","monaco_get_field_schemas","monaco_get_meeting","monaco_get_opportunity","monaco_get_sequence","monaco_get_task","monaco_link_opportunity_contacts","monaco_list_accounts","monaco_list_audience_contacts","monaco_list_audiences","monaco_list_campaigns","monaco_list_contacts","monaco_list_meetings","monaco_list_messages","monaco_list_opportunities","monaco_list_opportunity_contacts","monaco_list_pipelines","monaco_list_sequences","monaco_list_tags","monaco_list_tasks","monaco_list_users","monaco_remove_audience_contacts","monaco_unlink_opportunity_contacts","monaco_update_account","monaco_update_contact","monaco_update_opportunity","monaco_update_sequence","monaco_update_task","monaco_upsert_account","monaco_upsert_contact","monday_archive_item","monday_change_column_value","monday_create_board","monday_create_column","monday_create_group","monday_create_item","monday_create_subitem","monday_create_update","monday_delete_item","monday_duplicate_item","monday_get_board","monday_get_groups","monday_get_item","monday_get_items","monday_list_boards","monday_move_item_to_group","monday_search_items","monday_update_item","mongodb_delete","mongodb_execute","mongodb_insert","mongodb_introspect","mongodb_query","mongodb_update","mssql_delete","mssql_execute","mssql_insert","mssql_introspect","mssql_query","mssql_update","mysql_delete","mysql_execute","mysql_insert","mysql_introspect","mysql_query","mysql_update","neo4j_create","neo4j_delete","neo4j_execute","neo4j_introspect","neo4j_merge","neo4j_query","neo4j_update","netsuite_attach_record","netsuite_batch_create_records","netsuite_batch_delete_records","netsuite_batch_get_records","netsuite_batch_update_records","netsuite_batch_upsert_records","netsuite_create_record","netsuite_delete_record","netsuite_detach_record","netsuite_execute_action","netsuite_execute_dataset","netsuite_execute_suiteql","netsuite_get_async_result","netsuite_get_async_status","netsuite_get_governance_limits","netsuite_get_record","netsuite_get_record_form","netsuite_get_record_metadata","netsuite_get_select_options","netsuite_get_server_time","netsuite_get_subresource","netsuite_list_datasets","netsuite_list_record_types","netsuite_list_records","netsuite_transform_record","netsuite_update_record","netsuite_upsert_record","neverbounce_get_credits","neverbounce_verify_email","new_relic_create_deployment_event","new_relic_get_entity","new_relic_nrql_query","new_relic_search_entities","notion_add_database_row","notion_add_database_row_v2","notion_append_blocks","notion_append_blocks_v2","notion_create_comment","notion_create_comment_v2","notion_create_database","notion_create_database_v2","notion_create_page","notion_create_page_v2","notion_delete_block","notion_delete_block_v2","notion_list_comments","notion_list_comments_v2","notion_list_users","notion_list_users_v2","notion_query_database","notion_query_database_v2","notion_read","notion_read_database","notion_read_database_v2","notion_read_v2","notion_retrieve_block","notion_retrieve_block_children","notion_retrieve_block_children_v2","notion_retrieve_block_v2","notion_retrieve_user","notion_retrieve_user_v2","notion_search","notion_search_v2","notion_update_block","notion_update_block_v2","notion_update_page","notion_update_page_v2","notion_write","notion_write_v2","obsidian_append_active","obsidian_append_note","obsidian_append_periodic_note","obsidian_create_note","obsidian_delete_note","obsidian_execute_command","obsidian_get_active","obsidian_get_note","obsidian_get_periodic_note","obsidian_list_commands","obsidian_list_files","obsidian_open_file","obsidian_patch_active","obsidian_patch_note","obsidian_search","okta_activate_group_rule","okta_activate_user","okta_add_user_to_group","okta_assign_group_to_app","okta_assign_user_role","okta_assign_user_to_app","okta_clear_user_sessions","okta_create_group","okta_create_group_rule","okta_create_user","okta_deactivate_group_rule","okta_deactivate_user","okta_delete_group","okta_delete_group_rule","okta_delete_user","okta_enroll_factor","okta_get_app","okta_get_factor","okta_get_group","okta_get_group_rule","okta_get_logs","okta_get_session","okta_get_user","okta_list_app_groups","okta_list_app_users","okta_list_apps","okta_list_factors","okta_list_group_members","okta_list_group_rules","okta_list_groups","okta_list_user_roles","okta_list_users","okta_remove_group_from_app","okta_remove_user_from_app","okta_remove_user_from_group","okta_remove_user_role","okta_reset_all_factors","okta_reset_factor","okta_reset_password","okta_revoke_session","okta_suspend_user","okta_unsuspend_user","okta_update_group","okta_update_user","onedrive_copy","onedrive_create_folder","onedrive_create_share_link","onedrive_delete","onedrive_download","onedrive_get_drive_info","onedrive_get_item","onedrive_list","onedrive_move","onedrive_search","onedrive_upload","onepassword_create_item","onepassword_delete_item","onepassword_get_item","onepassword_get_item_file","onepassword_get_vault","onepassword_list_items","onepassword_list_vaults","onepassword_replace_item","onepassword_resolve_secret","onepassword_update_item","openai_embeddings","openai_image","oracle_fusion_financials_apply_receivables_receipt","oracle_fusion_financials_approve_receivables_invoice","oracle_fusion_financials_create_expense_line","oracle_fusion_financials_create_expense_report","oracle_fusion_financials_create_receivables_credit_memo","oracle_fusion_financials_create_receivables_invoice","oracle_fusion_financials_create_receivables_receipt","oracle_fusion_financials_get_expense_line","oracle_fusion_financials_get_expense_report","oracle_fusion_financials_get_gl_journal_batch","oracle_fusion_financials_get_gl_ledger","oracle_fusion_financials_get_payables_invoice","oracle_fusion_financials_get_payables_invoice_line","oracle_fusion_financials_get_payables_payment","oracle_fusion_financials_get_receivables_credit_memo","oracle_fusion_financials_get_receivables_invoice","oracle_fusion_financials_get_receivables_invoice_installment","oracle_fusion_financials_get_receivables_invoice_line","oracle_fusion_financials_get_receivables_receipt","oracle_fusion_financials_list_expense_lines","oracle_fusion_financials_list_expense_reports","oracle_fusion_financials_list_gl_balances","oracle_fusion_financials_list_gl_journal_batches","oracle_fusion_financials_list_gl_ledgers","oracle_fusion_financials_list_payables_invoice_lines","oracle_fusion_financials_list_payables_invoices","oracle_fusion_financials_list_payables_payments","oracle_fusion_financials_list_receivables_credit_memos","oracle_fusion_financials_list_receivables_invoice_installments","oracle_fusion_financials_list_receivables_invoice_lines","oracle_fusion_financials_list_receivables_invoices","oracle_fusion_financials_list_receivables_receipts","oracle_fusion_financials_submit_expense_report","oracle_fusion_financials_update_expense_line","oracle_fusion_financials_update_expense_report","oracle_fusion_financials_update_receivables_invoice","oracledb_delete","oracledb_execute","oracledb_insert","oracledb_introspect","oracledb_query","oracledb_update","otter_create_conversation","otter_get_conversation","otter_get_conversation_audio","otter_get_workspace","otter_list_channel_members","otter_list_channels","otter_list_conversations","otter_list_workspace_conversations","outlook_calendar_create_event","outlook_calendar_delete_event","outlook_calendar_get_event","outlook_calendar_list_events","outlook_calendar_respond","outlook_calendar_update_event","outlook_copy","outlook_create_folder","outlook_delete","outlook_draft","outlook_forward","outlook_get_attachment","outlook_list_attachments","outlook_list_folders","outlook_mark_read","outlook_mark_unread","outlook_move","outlook_read","outlook_reply","outlook_reply_all","outlook_search","outlook_send","outlook_update_message","pagerduty_add_note","pagerduty_create_incident","pagerduty_get_incident","pagerduty_get_service","pagerduty_list_escalation_policies","pagerduty_list_incident_alerts","pagerduty_list_incidents","pagerduty_list_oncalls","pagerduty_list_schedules","pagerduty_list_services","pagerduty_list_users","pagerduty_merge_incidents","pagerduty_send_event","pagerduty_snooze_incident","pagerduty_update_incident","parallel_deep_research","parallel_extract","parallel_search","pdl_autocomplete","pdl_bulk_company_enrich","pdl_bulk_person_enrich","pdl_clean_company","pdl_clean_location","pdl_clean_school","pdl_company_enrich","pdl_company_search","pdl_person_enrich","pdl_person_identify","pdl_person_search","perplexity_chat","perplexity_search","persona_approve_inquiry","persona_create_account","persona_create_inquiry","persona_create_report","persona_decline_inquiry","persona_expire_inquiry","persona_generate_inquiry_link","persona_get_account","persona_get_case","persona_get_document","persona_get_inquiry","persona_get_report","persona_get_verification","persona_import_accounts","persona_list_accounts","persona_list_cases","persona_list_inquiries","persona_list_inquiry_templates","persona_list_reports","persona_mark_inquiry_for_review","persona_print_inquiry_pdf","persona_redact_account","persona_redact_inquiry","persona_resume_inquiry","persona_update_account","persona_update_inquiry","pinecone_delete_vectors","pinecone_describe_index","pinecone_describe_index_stats","pinecone_fetch","pinecone_generate_embeddings","pinecone_list_indexes","pinecone_list_vector_ids","pinecone_search_text","pinecone_search_vector","pinecone_update_vector","pinecone_upsert_text","pipedrive_create_activity","pipedrive_create_deal","pipedrive_create_lead","pipedrive_create_project","pipedrive_delete_lead","pipedrive_get_activities","pipedrive_get_all_deals","pipedrive_get_deal","pipedrive_get_files","pipedrive_get_leads","pipedrive_get_mail_messages","pipedrive_get_mail_thread","pipedrive_get_pipeline_deals","pipedrive_get_pipelines","pipedrive_get_projects","pipedrive_update_activity","pipedrive_update_deal","pipedrive_update_lead","pitchbook_company_active_investors","pitchbook_company_bio","pitchbook_company_deal_service_providers","pitchbook_company_deals","pitchbook_company_financials","pitchbook_company_general_service_providers","pitchbook_company_industries","pitchbook_company_investors","pitchbook_company_most_recent_debt_financing","pitchbook_company_most_recent_financials","pitchbook_company_most_recent_financing","pitchbook_company_search","pitchbook_company_similar_companies","pitchbook_company_social_analytics","pitchbook_company_updates","pitchbook_company_vc_exit_predictions","pitchbook_contracts_history","pitchbook_cost_of_calls","pitchbook_credit_history","pitchbook_credit_news","pitchbook_credit_news_bulk","pitchbook_credit_news_most_recent","pitchbook_credit_news_search","pitchbook_deal_bio","pitchbook_deal_cap_table_history","pitchbook_deal_debt_lenders","pitchbook_deal_detailed","pitchbook_deal_investors","pitchbook_deal_multiples","pitchbook_deal_search","pitchbook_deal_service_providers","pitchbook_deal_stock_info","pitchbook_deal_tranche_info","pitchbook_deal_updates","pitchbook_deal_valuation","pitchbook_entity_affiliates","pitchbook_entity_locations","pitchbook_entity_news","pitchbook_entity_people","pitchbook_entity_updates","pitchbook_fund_active_investments","pitchbook_fund_benchmark","pitchbook_fund_bio","pitchbook_fund_cash_flows","pitchbook_fund_commitments","pitchbook_fund_investment_preferences","pitchbook_fund_investments","pitchbook_fund_performance","pitchbook_fund_search","pitchbook_fund_team","pitchbook_fund_updates","pitchbook_investor_active_investments","pitchbook_investor_bio","pitchbook_investor_board_seats","pitchbook_investor_deal_service_providers","pitchbook_investor_funds","pitchbook_investor_general_service_providers","pitchbook_investor_investments","pitchbook_investor_last_closed_fund","pitchbook_investor_preferences","pitchbook_investor_search","pitchbook_investor_updates","pitchbook_limited_partner_actual_allocations","pitchbook_limited_partner_bio","pitchbook_limited_partner_commitment_aggregates","pitchbook_limited_partner_commitment_preferences","pitchbook_limited_partner_commitments_detailed","pitchbook_limited_partner_search","pitchbook_limited_partner_service_providers","pitchbook_limited_partner_target_allocations","pitchbook_limited_partner_updates","pitchbook_lookup_table_structure","pitchbook_lookup_tables","pitchbook_patent_detailed","pitchbook_patent_search","pitchbook_people_search","pitchbook_person_bio","pitchbook_person_contact","pitchbook_person_education_work","pitchbook_sandbox_entities","pitchbook_search","pitchbook_service_provider_bio","pitchbook_service_provider_search","pitchbook_service_provider_updates","pitchbook_serviced_companies","pitchbook_serviced_deals","pitchbook_serviced_funds","pitchbook_serviced_investors","pitchbook_serviced_limited_partners","pitchbook_shared_search","pitchbook_usage_report","plane_archive_project","plane_archive_work_item","plane_confirm_attachment_upload","plane_create_attachment_upload","plane_create_comment","plane_create_cycle","plane_create_label","plane_create_link","plane_create_module","plane_create_project","plane_create_project_page","plane_create_state","plane_create_webhook","plane_create_work_item","plane_delete_attachment","plane_delete_comment","plane_delete_cycle","plane_delete_label","plane_delete_link","plane_delete_module","plane_delete_project","plane_delete_project_page","plane_delete_state","plane_delete_webhook","plane_delete_work_item","plane_get_attachment","plane_get_comment","plane_get_current_user","plane_get_cycle","plane_get_label","plane_get_link","plane_get_module","plane_get_project","plane_get_project_page","plane_get_project_summary","plane_get_state","plane_get_webhook","plane_get_work_item","plane_get_work_item_by_identifier","plane_list_attachments","plane_list_comments","plane_list_cycles","plane_list_labels","plane_list_links","plane_list_modules","plane_list_project_pages","plane_list_projects","plane_list_states","plane_list_webhooks","plane_list_work_items","plane_list_workspace_work_items","plane_manage_cycle_work_items","plane_manage_module_work_items","plane_regenerate_webhook_secret","plane_transfer_cycle_work_items","plane_unarchive_project","plane_unarchive_work_item","plane_update_comment","plane_update_cycle","plane_update_label","plane_update_link","plane_update_module","plane_update_project","plane_update_project_page","plane_update_state","plane_update_webhook","plane_update_work_item","planetscale_close_deploy_request","planetscale_create_backup","planetscale_create_branch","planetscale_create_deploy_request","planetscale_delete_branch","planetscale_get_backup","planetscale_get_branch","planetscale_get_database","planetscale_get_deploy_request","planetscale_list_backups","planetscale_list_branches","planetscale_list_databases","planetscale_list_deploy_requests","planetscale_queue_deploy_request","planetscale_review_deploy_request","polymarket_get_activity","polymarket_get_event","polymarket_get_events","polymarket_get_holders","polymarket_get_last_trade_price","polymarket_get_leaderboard","polymarket_get_market","polymarket_get_markets","polymarket_get_midpoint","polymarket_get_orderbook","polymarket_get_positions","polymarket_get_price","polymarket_get_price_history","polymarket_get_series","polymarket_get_series_by_id","polymarket_get_spread","polymarket_get_tags","polymarket_get_tick_size","polymarket_get_trades","polymarket_search","postgresql_delete","postgresql_execute","postgresql_insert","postgresql_introspect","postgresql_query","postgresql_update","posthog_batch_events","posthog_capture_event","posthog_create_annotation","posthog_create_cohort","posthog_create_dashboard","posthog_create_experiment","posthog_create_feature_flag","posthog_create_insight","posthog_create_survey","posthog_delete_feature_flag","posthog_delete_person","posthog_delete_survey","posthog_evaluate_flags","posthog_get_cohort","posthog_get_dashboard","posthog_get_event_definition","posthog_get_experiment","posthog_get_feature_flag","posthog_get_insight","posthog_get_organization","posthog_get_person","posthog_get_project","posthog_get_property_definition","posthog_get_session_recording","posthog_get_survey","posthog_list_actions","posthog_list_annotations","posthog_list_cohorts","posthog_list_dashboards","posthog_list_event_definitions","posthog_list_experiments","posthog_list_feature_flags","posthog_list_insights","posthog_list_organizations","posthog_list_persons","posthog_list_projects","posthog_list_property_definitions","posthog_list_recording_playlists","posthog_list_session_recordings","posthog_list_surveys","posthog_query","posthog_update_cohort","posthog_update_event_definition","posthog_update_experiment","posthog_update_feature_flag","posthog_update_insight","posthog_update_property_definition","posthog_update_survey","powerbi_execute_query","powerbi_get_dataset","powerbi_get_refresh_history","powerbi_get_report","powerbi_list_datasets","powerbi_list_reports","powerbi_list_workspaces","powerbi_refresh_dataset","profound_bot_logs","profound_bots_report","profound_category_assets","profound_category_personas","profound_category_prompts","profound_category_tags","profound_category_topics","profound_citation_prompts","profound_citations_report","profound_list_assets","profound_list_categories","profound_list_domains","profound_list_models","profound_list_optimizations","profound_list_personas","profound_list_regions","profound_optimization_analysis","profound_prompt_answers","profound_prompt_volume","profound_query_fanouts","profound_raw_logs","profound_referrals_report","profound_sentiment_report","profound_visibility_report","prospeo_account_information","prospeo_bulk_enrich_company","prospeo_bulk_enrich_person","prospeo_enrich_company","prospeo_enrich_person","prospeo_search_company","prospeo_search_person","prospeo_search_suggestions","pulse_parser","pulse_parser_v2","qdrant_fetch_points","qdrant_search_vector","qdrant_upsert_points","quartr_get_audio","quartr_get_company","quartr_get_event","quartr_get_event_summary","quartr_get_report","quartr_get_slide_deck","quartr_get_transcript","quartr_list_audio","quartr_list_companies","quartr_list_document_types","quartr_list_documents","quartr_list_event_types","quartr_list_events","quartr_list_live_events","quartr_list_reports","quartr_list_slide_decks","quartr_list_transcripts","quickbooks_add_attachment","quickbooks_create_bill","quickbooks_create_bill_payment","quickbooks_create_credit_memo","quickbooks_create_customer","quickbooks_create_customer_payment","quickbooks_create_deposit","quickbooks_create_employee","quickbooks_create_estimate","quickbooks_create_invoice","quickbooks_create_item","quickbooks_create_journal_entry","quickbooks_create_purchase","quickbooks_create_purchase_order","quickbooks_create_refund_receipt","quickbooks_create_sales_receipt","quickbooks_create_vendor","quickbooks_create_vendor_credit","quickbooks_download_attachment","quickbooks_download_transaction_pdf","quickbooks_email_transaction","quickbooks_get_company_info","quickbooks_read_accounting_transactions","quickbooks_read_attachments","quickbooks_read_master_data","quickbooks_read_purchasing_transactions","quickbooks_read_sales_transactions","quickbooks_run_financial_report","quickbooks_update_bill","quickbooks_update_bill_payment","quickbooks_update_credit_memo","quickbooks_update_customer","quickbooks_update_customer_payment","quickbooks_update_deposit","quickbooks_update_employee","quickbooks_update_estimate","quickbooks_update_invoice","quickbooks_update_item","quickbooks_update_journal_entry","quickbooks_update_purchase","quickbooks_update_purchase_order","quickbooks_update_refund_receipt","quickbooks_update_sales_receipt","quickbooks_update_vendor","quickbooks_update_vendor_credit","quickbooks_void_bill_payment","quickbooks_void_customer_payment","quickbooks_void_invoice","quickbooks_void_sales_receipt","quiver_image_to_svg","quiver_image_to_svg_v2","quiver_list_models","quiver_text_to_svg","quiver_text_to_svg_v2","rabbitmq_create_binding","rabbitmq_create_exchange","rabbitmq_create_policy","rabbitmq_create_queue","rabbitmq_delete_binding","rabbitmq_delete_exchange","rabbitmq_delete_policy","rabbitmq_delete_queue","rabbitmq_get_exchange","rabbitmq_get_messages","rabbitmq_get_overview","rabbitmq_get_queue","rabbitmq_health_check","rabbitmq_list_bindings","rabbitmq_list_channels","rabbitmq_list_connections","rabbitmq_list_consumers","rabbitmq_list_exchange_bindings","rabbitmq_list_exchanges","rabbitmq_list_nodes","rabbitmq_list_policies","rabbitmq_list_queues","rabbitmq_list_vhosts","rabbitmq_publish_message","rabbitmq_purge_queue","railway_create_environment","railway_create_project","railway_create_service","railway_delete_environment","railway_delete_project","railway_delete_service","railway_delete_variable","railway_deploy_service","railway_get_deployment","railway_get_deployment_logs","railway_get_project","railway_list_deployments","railway_list_project_members","railway_list_projects","railway_list_variables","railway_restart_deployment","railway_rollback_deployment","railway_transfer_project","railway_update_project","railway_upsert_variable","ramp_create_memo","ramp_get_balance","ramp_get_bill","ramp_get_business","ramp_get_memo","ramp_get_physical_card","ramp_get_reimbursement","ramp_get_transaction","ramp_get_user","ramp_get_vendor","ramp_get_virtual_card","ramp_list_bills","ramp_list_physical_cards","ramp_list_reimbursements","ramp_list_transactions","ramp_list_users","ramp_list_vendors","ramp_list_virtual_cards","rb2b_credit_check","rb2b_email_to_activity","rb2b_hem_to_best_linkedin","rb2b_hem_to_business_profile","rb2b_hem_to_linkedin","rb2b_hem_to_maid","rb2b_ip_to_company","rb2b_ip_to_hem","rb2b_ip_to_maid","rb2b_linkedin_slug_search","rb2b_linkedin_to_best_personal_email","rb2b_linkedin_to_business_profile","rb2b_linkedin_to_hashed_emails","rb2b_linkedin_to_mobile_phone","rb2b_linkedin_to_personal_email","rds_delete","rds_execute","rds_insert","rds_introspect","rds_query","rds_update","reddit_delete","reddit_edit","reddit_get_comments","reddit_get_controversial","reddit_get_info","reddit_get_me","reddit_get_messages","reddit_get_posts","reddit_get_saved","reddit_get_subreddit_info","reddit_get_subreddit_rules","reddit_get_user","reddit_get_user_comments","reddit_get_user_posts","reddit_hide","reddit_hot_posts","reddit_list_my_subreddits","reddit_lock","reddit_mark_all_read","reddit_mark_read","reddit_marknsfw","reddit_mod_approve","reddit_mod_distinguish","reddit_mod_remove","reddit_mod_sticky","reddit_reply","reddit_report","reddit_save","reddit_search","reddit_search_subreddits","reddit_send_message","reddit_submit_post","reddit_subscribe","reddit_unhide","reddit_unlock","reddit_unmarknsfw","reddit_unsave","reddit_vote","redis_command","redis_delete","redis_exists","redis_expire","redis_get","redis_hdel","redis_hget","redis_hgetall","redis_hset","redis_incr","redis_incrby","redis_keys","redis_llen","redis_lpop","redis_lpush","redis_lrange","redis_persist","redis_rpop","redis_rpush","redis_set","redis_setnx","redis_ttl","reducto_parser","reducto_parser_v2","resend_cancel_email","resend_create_audience","resend_create_broadcast","resend_create_contact","resend_delete_audience","resend_delete_contact","resend_get_audience","resend_get_broadcast","resend_get_contact","resend_get_email","resend_list_audiences","resend_list_contacts","resend_list_domains","resend_send","resend_send_broadcast","resend_update_contact","revenuecat_create_purchase","revenuecat_defer_google_subscription","revenuecat_delete_customer","revenuecat_get_customer","revenuecat_grant_entitlement","revenuecat_list_offerings","revenuecat_refund_google_subscription","revenuecat_revoke_entitlement","revenuecat_revoke_google_subscription","revenuecat_update_subscriber_attributes","rippling_bulk_create_custom_object_records","rippling_bulk_delete_custom_object_records","rippling_bulk_update_custom_object_records","rippling_create_business_partner","rippling_create_business_partner_group","rippling_create_custom_app","rippling_create_custom_object","rippling_create_custom_object_field","rippling_create_custom_object_record","rippling_create_custom_page","rippling_create_custom_setting","rippling_create_department","rippling_create_draft_hires","rippling_create_object_category","rippling_create_title","rippling_create_work_location","rippling_delete_business_partner","rippling_delete_business_partner_group","rippling_delete_custom_app","rippling_delete_custom_object","rippling_delete_custom_object_field","rippling_delete_custom_object_record","rippling_delete_custom_page","rippling_delete_custom_setting","rippling_delete_object_category","rippling_delete_title","rippling_delete_work_location","rippling_get_business_partner","rippling_get_business_partner_group","rippling_get_current_user","rippling_get_custom_app","rippling_get_custom_object","rippling_get_custom_object_field","rippling_get_custom_object_record","rippling_get_custom_object_record_by_external_id","rippling_get_custom_page","rippling_get_custom_setting","rippling_get_department","rippling_get_employment_type","rippling_get_job_function","rippling_get_object_category","rippling_get_report_run","rippling_get_supergroup","rippling_get_team","rippling_get_title","rippling_get_user","rippling_get_work_location","rippling_get_worker","rippling_list_business_partner_groups","rippling_list_business_partners","rippling_list_companies","rippling_list_custom_apps","rippling_list_custom_fields","rippling_list_custom_object_fields","rippling_list_custom_object_records","rippling_list_custom_objects","rippling_list_custom_pages","rippling_list_custom_settings","rippling_list_departments","rippling_list_employment_types","rippling_list_entitlements","rippling_list_job_functions","rippling_list_object_categories","rippling_list_supergroup_exclusion_members","rippling_list_supergroup_inclusion_members","rippling_list_supergroup_members","rippling_list_supergroups","rippling_list_teams","rippling_list_titles","rippling_list_users","rippling_list_work_locations","rippling_list_workers","rippling_query_custom_object_records","rippling_trigger_report_run","rippling_update_custom_app","rippling_update_custom_object","rippling_update_custom_object_field","rippling_update_custom_object_record","rippling_update_custom_page","rippling_update_custom_setting","rippling_update_department","rippling_update_object_category","rippling_update_supergroup_exclusion_members","rippling_update_supergroup_inclusion_members","rippling_update_title","rippling_update_work_location","rocketlane_add_field_option","rocketlane_add_project_members","rocketlane_add_task_assignees","rocketlane_add_task_dependencies","rocketlane_add_task_followers","rocketlane_archive_project","rocketlane_assign_placeholders","rocketlane_create_field","rocketlane_create_phase","rocketlane_create_project","rocketlane_create_space","rocketlane_create_space_document","rocketlane_create_task","rocketlane_create_time_entry","rocketlane_create_time_off","rocketlane_delete_field","rocketlane_delete_phase","rocketlane_delete_project","rocketlane_delete_space","rocketlane_delete_space_document","rocketlane_delete_task","rocketlane_delete_time_entry","rocketlane_delete_time_off","rocketlane_get_field","rocketlane_get_invoice","rocketlane_get_invoice_line_items","rocketlane_get_invoice_payments","rocketlane_get_phase","rocketlane_get_project","rocketlane_get_space","rocketlane_get_space_document","rocketlane_get_task","rocketlane_get_time_entry","rocketlane_get_time_off","rocketlane_get_user","rocketlane_import_template","rocketlane_list_fields","rocketlane_list_invoices","rocketlane_list_phases","rocketlane_list_placeholders","rocketlane_list_projects","rocketlane_list_resource_allocations","rocketlane_list_space_documents","rocketlane_list_spaces","rocketlane_list_tasks","rocketlane_list_time_entries","rocketlane_list_time_entry_categories","rocketlane_list_time_offs","rocketlane_list_users","rocketlane_move_task_to_phase","rocketlane_remove_project_members","rocketlane_remove_task_assignees","rocketlane_remove_task_dependencies","rocketlane_remove_task_followers","rocketlane_search_time_entries","rocketlane_unassign_placeholders","rocketlane_update_field","rocketlane_update_field_option","rocketlane_update_phase","rocketlane_update_project","rocketlane_update_space","rocketlane_update_space_document","rocketlane_update_task","rocketlane_update_time_entry","rootly_acknowledge_alert","rootly_add_incident_event","rootly_add_subscribers","rootly_assign_incident_role","rootly_create_action_item","rootly_create_alert","rootly_create_incident","rootly_create_status_page_event","rootly_delete_action_item","rootly_delete_incident","rootly_escalate_alert","rootly_get_alert","rootly_get_incident","rootly_list_action_items","rootly_list_alerts","rootly_list_causes","rootly_list_environments","rootly_list_escalation_policies","rootly_list_functionalities","rootly_list_incident_events","rootly_list_incident_roles","rootly_list_incident_types","rootly_list_incidents","rootly_list_on_calls","rootly_list_playbooks","rootly_list_retrospectives","rootly_list_schedules","rootly_list_services","rootly_list_severities","rootly_list_teams","rootly_list_users","rootly_mitigate_incident","rootly_remove_subscribers","rootly_resolve_alert","rootly_resolve_incident","rootly_run_workflow","rootly_snooze_alert","rootly_unassign_incident_role","rootly_update_action_item","rootly_update_alert","rootly_update_incident","s3_copy_object","s3_create_bucket","s3_delete_bucket","s3_delete_object","s3_delete_objects","s3_get_object","s3_head_object","s3_list_buckets","s3_list_objects","s3_presigned_url","s3_put_object","sailpoint_approve_access_request","sailpoint_cancel_access_request","sailpoint_decide_certification_review_items","sailpoint_get_access_profile","sailpoint_get_access_profile_entitlements","sailpoint_get_access_request_config","sailpoint_get_access_request_status","sailpoint_get_account","sailpoint_get_account_activity","sailpoint_get_account_entitlements","sailpoint_get_account_selections","sailpoint_get_campaign","sailpoint_get_certification","sailpoint_get_entitlement","sailpoint_get_entitlement_request_config","sailpoint_get_identity","sailpoint_get_role","sailpoint_get_role_entitlements","sailpoint_get_source","sailpoint_get_task_status","sailpoint_list_access_profiles","sailpoint_list_account_activities","sailpoint_list_accounts","sailpoint_list_campaigns","sailpoint_list_certification_review_items","sailpoint_list_certifications","sailpoint_list_entitlements","sailpoint_list_identities","sailpoint_list_identity_entitlements","sailpoint_list_pending_access_request_approvals","sailpoint_list_roles","sailpoint_list_sources","sailpoint_load_accounts","sailpoint_load_entitlements","sailpoint_reject_access_request","sailpoint_request_access","sailpoint_search","sailpoint_search_aggregate","sailpoint_search_count","sailpoint_sign_off_certification","salesforce_create_account","salesforce_create_case","salesforce_create_contact","salesforce_create_custom_field","salesforce_create_custom_object","salesforce_create_lead","salesforce_create_opportunity","salesforce_create_task","salesforce_delete_account","salesforce_delete_case","salesforce_delete_contact","salesforce_delete_custom_field","salesforce_delete_lead","salesforce_delete_opportunity","salesforce_delete_task","salesforce_describe_object","salesforce_get_accounts","salesforce_get_cases","salesforce_get_contacts","salesforce_get_dashboard","salesforce_get_leads","salesforce_get_opportunities","salesforce_get_report","salesforce_get_tasks","salesforce_list_dashboards","salesforce_list_objects","salesforce_list_report_types","salesforce_list_reports","salesforce_query","salesforce_query_more","salesforce_refresh_dashboard","salesforce_run_report","salesforce_tooling_query","salesforce_update_account","salesforce_update_case","salesforce_update_contact","salesforce_update_custom_field","salesforce_update_lead","salesforce_update_opportunity","salesforce_update_task","sap_concur_approve_expense_report","sap_concur_associate_attendees","sap_concur_create_cash_advance","sap_concur_create_expected_expense","sap_concur_create_expense_report","sap_concur_create_list_item","sap_concur_create_purchase_request","sap_concur_create_quick_expense","sap_concur_create_quick_expense_with_image","sap_concur_create_report_comment","sap_concur_create_travel_request","sap_concur_create_user","sap_concur_delete_expected_expense","sap_concur_delete_expense","sap_concur_delete_expense_report","sap_concur_delete_list_item","sap_concur_delete_travel_request","sap_concur_delete_user","sap_concur_get_allocation","sap_concur_get_budget","sap_concur_get_cash_advance","sap_concur_get_expected_expense","sap_concur_get_expense","sap_concur_get_expense_report","sap_concur_get_itemizations","sap_concur_get_itinerary","sap_concur_get_list","sap_concur_get_list_item","sap_concur_get_purchase_request","sap_concur_get_receipt","sap_concur_get_receipt_status","sap_concur_get_request_cash_advance","sap_concur_get_travel_profile","sap_concur_get_travel_request","sap_concur_get_user","sap_concur_issue_cash_advance","sap_concur_list_allocations","sap_concur_list_attendee_associations","sap_concur_list_budget_categories","sap_concur_list_budgets","sap_concur_list_exceptions","sap_concur_list_expected_expenses","sap_concur_list_expense_reports","sap_concur_list_expenses","sap_concur_list_itineraries","sap_concur_list_list_items","sap_concur_list_lists","sap_concur_list_receipts","sap_concur_list_report_comments","sap_concur_list_reports_to_approve","sap_concur_list_travel_profiles_summary","sap_concur_list_travel_request_comments","sap_concur_list_travel_requests","sap_concur_list_users","sap_concur_move_travel_request","sap_concur_recall_expense_report","sap_concur_remove_all_attendees","sap_concur_search_locations","sap_concur_search_users","sap_concur_send_back_expense_report","sap_concur_submit_expense_report","sap_concur_update_allocation","sap_concur_update_expected_expense","sap_concur_update_expense","sap_concur_update_expense_report","sap_concur_update_list_item","sap_concur_update_travel_request","sap_concur_update_user","sap_concur_upload_exchange_rates","sap_concur_upload_receipt_image","sap_s4hana_create_business_partner","sap_s4hana_create_purchase_order","sap_s4hana_create_purchase_requisition","sap_s4hana_create_sales_order","sap_s4hana_delete_sales_order","sap_s4hana_get_billing_document","sap_s4hana_get_business_partner","sap_s4hana_get_customer","sap_s4hana_get_inbound_delivery","sap_s4hana_get_material_document","sap_s4hana_get_outbound_delivery","sap_s4hana_get_product","sap_s4hana_get_purchase_order","sap_s4hana_get_purchase_requisition","sap_s4hana_get_sales_order","sap_s4hana_get_supplier","sap_s4hana_get_supplier_invoice","sap_s4hana_list_billing_documents","sap_s4hana_list_business_partners","sap_s4hana_list_customers","sap_s4hana_list_inbound_deliveries","sap_s4hana_list_material_documents","sap_s4hana_list_material_stock","sap_s4hana_list_outbound_deliveries","sap_s4hana_list_products","sap_s4hana_list_purchase_orders","sap_s4hana_list_purchase_requisitions","sap_s4hana_list_sales_orders","sap_s4hana_list_supplier_invoices","sap_s4hana_list_suppliers","sap_s4hana_odata_query","sap_s4hana_update_business_partner","sap_s4hana_update_customer","sap_s4hana_update_product","sap_s4hana_update_purchase_order","sap_s4hana_update_purchase_requisition","sap_s4hana_update_sales_order","sap_s4hana_update_supplier","search_tool","secrets_manager_create_secret","secrets_manager_delete_secret","secrets_manager_describe_secret","secrets_manager_get_secret","secrets_manager_list_secrets","secrets_manager_restore_secret","secrets_manager_rotate_secret","secrets_manager_tag_resource","secrets_manager_untag_resource","secrets_manager_update_secret","semrush_backlinks","semrush_backlinks_anchors","semrush_backlinks_competitors","semrush_backlinks_geo_distribution","semrush_backlinks_indexed_pages","semrush_backlinks_overview","semrush_backlinks_tld_distribution","semrush_batch_keyword_overview","semrush_broad_match_keywords","semrush_domain_ad_copies","semrush_domain_ad_history","semrush_domain_organic_competitors","semrush_domain_organic_keywords","semrush_domain_overview","semrush_domain_overview_all","semrush_domain_overview_history","semrush_domain_paid_competitors","semrush_domain_paid_keywords","semrush_domain_pla_copies","semrush_domain_pla_keywords","semrush_domain_vs_domain","semrush_keyword_ad_history","semrush_keyword_difficulty","semrush_keyword_overview","semrush_keyword_overview_all","semrush_keyword_questions","semrush_organic_results","semrush_paid_results","semrush_referring_domains","semrush_referring_ips","semrush_related_keywords","semrush_subdomain_ad_copies","semrush_subdomain_organic_keywords","semrush_subdomain_overview","semrush_subdomain_overview_all","semrush_subdomain_overview_history","semrush_subdomain_paid_keywords","semrush_top_domains","semrush_url_organic_keywords","semrush_url_overview","semrush_url_overview_all","semrush_url_overview_history","semrush_url_paid_keywords","semrush_winners_and_losers","sendblue_evaluate_service","sendblue_get_message","sendblue_send_group_message","sendblue_send_message","sendblue_send_typing_indicator","sendgrid_add_contact","sendgrid_add_contacts_to_list","sendgrid_create_list","sendgrid_create_template","sendgrid_create_template_version","sendgrid_delete_contacts","sendgrid_delete_list","sendgrid_delete_template","sendgrid_get_contact","sendgrid_get_list","sendgrid_get_template","sendgrid_list_all_lists","sendgrid_list_templates","sendgrid_remove_contacts_from_list","sendgrid_search_contacts","sendgrid_send_mail","sentry_events_get","sentry_events_list","sentry_issues_get","sentry_issues_list","sentry_issues_update","sentry_projects_create","sentry_projects_get","sentry_projects_list","sentry_projects_update","sentry_releases_create","sentry_releases_deploy","sentry_releases_list","sentry_teams_list","serper_search","servicenow_add_incident_comment","servicenow_aggregate","servicenow_close_incident","servicenow_create_change_request","servicenow_create_incident","servicenow_create_record","servicenow_delete_record","servicenow_download_attachment","servicenow_download_attachment_v2","servicenow_find_user","servicenow_get_change_next_states","servicenow_get_change_request","servicenow_get_ci","servicenow_get_incident","servicenow_get_knowledge_article","servicenow_get_requested_item","servicenow_list_approvals","servicenow_list_attachments","servicenow_list_catalog_items","servicenow_list_change_requests","servicenow_list_change_tasks","servicenow_list_ci_relationships","servicenow_list_group_members","servicenow_list_incidents","servicenow_list_requested_items","servicenow_order_catalog_item","servicenow_read_record","servicenow_resolve_incident","servicenow_search_cis","servicenow_search_knowledge","servicenow_update_approval","servicenow_update_change_request","servicenow_update_change_state","servicenow_update_incident","servicenow_update_record","servicenow_upload_attachment","ses_create_configuration_set","ses_create_email_identity","ses_create_template","ses_delete_email_identity","ses_delete_suppressed_destination","ses_delete_template","ses_get_account","ses_get_email_identity","ses_get_suppressed_destination","ses_get_template","ses_list_identities","ses_list_suppressed_destinations","ses_list_templates","ses_put_suppressed_destination","ses_send_bulk_email","ses_send_custom_verification_email","ses_send_email","ses_send_templated_email","ses_update_template","sftp_delete","sftp_download","sftp_download_v2","sftp_list","sftp_mkdir","sftp_upload","sharepoint_add_list_items","sharepoint_create_list","sharepoint_create_page","sharepoint_delete_file","sharepoint_delete_list_item","sharepoint_delete_page","sharepoint_download_file","sharepoint_get_drive_item","sharepoint_get_list","sharepoint_get_list_item","sharepoint_list_sites","sharepoint_publish_page","sharepoint_read_page","sharepoint_update_list","sharepoint_update_page","sharepoint_upload_file","shopify_adjust_inventory","shopify_cancel_order","shopify_create_customer","shopify_create_fulfillment","shopify_create_product","shopify_create_product_variants","shopify_delete_customer","shopify_delete_product","shopify_get_collection","shopify_get_customer","shopify_get_fulfillment","shopify_get_fulfillment_order","shopify_get_inventory_level","shopify_get_job","shopify_get_order","shopify_get_product","shopify_list_collections","shopify_list_customers","shopify_list_fulfillment_orders","shopify_list_inventory_items","shopify_list_locations","shopify_list_orders","shopify_list_products","shopify_update_customer","shopify_update_fulfillment_tracking","shopify_update_order","shopify_update_product","shopify_update_product_variants","similarweb_bounce_rate","similarweb_page_views","similarweb_pages_per_visit","similarweb_traffic_visits","similarweb_visit_duration","similarweb_website_overview","sixtyfour_enrich_company","sixtyfour_enrich_lead","sixtyfour_find_email","sixtyfour_find_phone","slack_add_bookmark","slack_add_reaction","slack_archive_conversation","slack_canvas","slack_close_conversation","slack_create_channel_canvas","slack_create_conversation","slack_create_user_group","slack_delete_canvas","slack_delete_file","slack_delete_message","slack_delete_scheduled_message","slack_disable_user_group","slack_download","slack_edit_bookmark","slack_edit_canvas","slack_enable_user_group","slack_ephemeral_message","slack_get_canvas","slack_get_channel_history","slack_get_channel_info","slack_get_dnd_info","slack_get_file_info","slack_get_message","slack_get_permalink","slack_get_reactions","slack_get_team_dnd_info","slack_get_team_info","slack_get_team_profile","slack_get_thread","slack_get_thread_replies","slack_get_user","slack_get_user_presence","slack_get_user_profile","slack_invite_to_conversation","slack_join_conversation","slack_kick_conversation","slack_leave_conversation","slack_list_bookmarks","slack_list_canvases","slack_list_channels","slack_list_emoji","slack_list_files","slack_list_members","slack_list_pins","slack_list_reactions","slack_list_scheduled_messages","slack_list_user_conversations","slack_list_user_group_members","slack_list_user_groups","slack_list_users","slack_lists_access_delete","slack_lists_access_set","slack_lists_create","slack_lists_download_get","slack_lists_download_start","slack_lists_items_create","slack_lists_items_delete","slack_lists_items_delete_multiple","slack_lists_items_info","slack_lists_items_list","slack_lists_items_update","slack_lists_update","slack_lookup_canvas_sections","slack_lookup_user_by_email","slack_mark_conversation_read","slack_message","slack_message_reader","slack_open_conversation","slack_open_view","slack_pin_message","slack_publish_view","slack_push_view","slack_remove_bookmark","slack_remove_reaction","slack_rename_agent_session_v2","slack_rename_conversation","slack_revoke_canvas_access","slack_schedule_message","slack_set_agent_session_status_v2","slack_set_conversation_purpose","slack_set_conversation_topic","slack_set_status","slack_set_suggested_prompts","slack_set_suggested_prompts_v2","slack_set_title","slack_set_user_presence","slack_share_canvas","slack_unarchive_conversation","slack_unfurl_links","slack_unpin_message","slack_update_message","slack_update_user_group","slack_update_user_group_members","slack_update_view","smartlead_add_email_accounts_to_campaign","smartlead_add_leads_to_campaign","smartlead_create_campaign","smartlead_create_lead_list","smartlead_delete_campaign","smartlead_delete_campaign_webhook","smartlead_delete_lead_from_campaign","smartlead_delete_lead_list","smartlead_duplicate_campaign","smartlead_export_campaign_leads","smartlead_get_campaign","smartlead_get_campaign_analytics","smartlead_get_campaign_analytics_by_date","smartlead_get_campaign_lead_statistics","smartlead_get_campaign_mailbox_statistics","smartlead_get_campaign_sequences","smartlead_get_campaign_statistics","smartlead_get_campaign_top_level_analytics_by_date","smartlead_get_campaign_webhook_summary","smartlead_get_lead_by_email","smartlead_get_lead_by_id","smartlead_get_lead_list","smartlead_get_lead_message_history","smartlead_list_campaign_email_accounts","smartlead_list_campaign_leads","smartlead_list_campaign_webhooks","smartlead_list_campaigns","smartlead_list_clients","smartlead_list_email_accounts","smartlead_list_inbox_replies","smartlead_list_lead_activities","smartlead_list_lead_categories","smartlead_list_lead_lists","smartlead_mark_lead_complete","smartlead_pause_lead","smartlead_remove_email_accounts_from_campaign","smartlead_resume_lead","smartlead_save_campaign_sequences","smartlead_unsubscribe_lead_from_campaign","smartlead_unsubscribe_lead_globally","smartlead_update_campaign_schedule","smartlead_update_campaign_settings","smartlead_update_campaign_status","smartlead_update_lead","smartlead_update_lead_category","smartlead_update_lead_list","smartlead_upsert_campaign_webhook","sms_send","smtp_send_mail","snowflake_alter_warehouse","snowflake_call_procedure","snowflake_cancel_statement","snowflake_cancel_task_run","snowflake_cortex_analyst_ask","snowflake_cortex_analyst_feedback","snowflake_delete_rows","snowflake_execute_sql","snowflake_get_statement","snowflake_get_task","snowflake_get_task_run","snowflake_get_task_run_output","snowflake_get_warehouse","snowflake_insert_rows","snowflake_introspect_schema","snowflake_list_copy_history","snowflake_list_databases","snowflake_list_query_history","snowflake_list_schemas","snowflake_list_tables","snowflake_list_task_runs","snowflake_list_tasks","snowflake_list_warehouses","snowflake_load_data","snowflake_resume_task","snowflake_resume_warehouse","snowflake_run_task","snowflake_suspend_task","snowflake_suspend_warehouse","snowflake_unload_data","snowflake_update_rows","snowflake_upsert_rows","splunk_cancel_search_job","splunk_create_search_job","splunk_dispatch_saved_search","splunk_get_fired_alerts","splunk_get_saved_search","splunk_get_search_job","splunk_get_search_results","splunk_list_apps","splunk_list_fired_alerts","splunk_list_indexes","splunk_list_saved_searches","splunk_run_search","sportmonks_core_get_cities","sportmonks_core_get_city","sportmonks_core_get_continent","sportmonks_core_get_continents","sportmonks_core_get_countries","sportmonks_core_get_country","sportmonks_core_get_entity_filters","sportmonks_core_get_my_usage","sportmonks_core_get_region","sportmonks_core_get_regions","sportmonks_core_get_timezones","sportmonks_core_get_type","sportmonks_core_get_type_by_entity","sportmonks_core_get_types","sportmonks_core_search_cities","sportmonks_core_search_countries","sportmonks_core_search_regions","sportmonks_football_expected_by_player","sportmonks_football_expected_by_team","sportmonks_football_get_all_commentaries","sportmonks_football_get_all_fixtures","sportmonks_football_get_all_players","sportmonks_football_get_all_rivals","sportmonks_football_get_all_teams","sportmonks_football_get_all_transfer_rumours","sportmonks_football_get_all_transfers","sportmonks_football_get_brackets_by_season","sportmonks_football_get_coach","sportmonks_football_get_coaches","sportmonks_football_get_coaches_by_country","sportmonks_football_get_commentaries_by_fixture","sportmonks_football_get_current_leagues_by_team","sportmonks_football_get_expected_lineups_by_player","sportmonks_football_get_expected_lineups_by_team","sportmonks_football_get_extended_team_squad","sportmonks_football_get_fixture","sportmonks_football_get_fixtures_by_date","sportmonks_football_get_fixtures_by_date_range","sportmonks_football_get_fixtures_by_date_range_for_team","sportmonks_football_get_fixtures_by_ids","sportmonks_football_get_grouped_standings_by_round","sportmonks_football_get_head_to_head","sportmonks_football_get_inplay_livescores","sportmonks_football_get_latest_coaches","sportmonks_football_get_latest_fixtures","sportmonks_football_get_latest_livescores","sportmonks_football_get_latest_players","sportmonks_football_get_latest_totw","sportmonks_football_get_latest_transfers","sportmonks_football_get_league","sportmonks_football_get_leagues","sportmonks_football_get_leagues_by_country","sportmonks_football_get_leagues_by_date","sportmonks_football_get_leagues_by_team","sportmonks_football_get_live_leagues","sportmonks_football_get_live_probabilities","sportmonks_football_get_live_probabilities_by_fixture","sportmonks_football_get_live_standings_by_league","sportmonks_football_get_livescores","sportmonks_football_get_match_facts","sportmonks_football_get_match_facts_by_date_range","sportmonks_football_get_match_facts_by_fixture","sportmonks_football_get_match_facts_by_league","sportmonks_football_get_past_fixtures_by_tv_station","sportmonks_football_get_player","sportmonks_football_get_players_by_country","sportmonks_football_get_postmatch_news","sportmonks_football_get_postmatch_news_by_season","sportmonks_football_get_predictability_by_league","sportmonks_football_get_prematch_news","sportmonks_football_get_prematch_news_by_season","sportmonks_football_get_prematch_news_upcoming","sportmonks_football_get_probabilities","sportmonks_football_get_probabilities_by_fixture","sportmonks_football_get_referee","sportmonks_football_get_referees","sportmonks_football_get_referees_by_country","sportmonks_football_get_referees_by_season","sportmonks_football_get_rivals_by_team","sportmonks_football_get_round","sportmonks_football_get_round_statistics","sportmonks_football_get_rounds","sportmonks_football_get_rounds_by_season","sportmonks_football_get_schedules_by_season","sportmonks_football_get_schedules_by_season_and_team","sportmonks_football_get_schedules_by_team","sportmonks_football_get_season","sportmonks_football_get_seasons","sportmonks_football_get_seasons_by_team","sportmonks_football_get_stage","sportmonks_football_get_stage_statistics","sportmonks_football_get_stages","sportmonks_football_get_stages_by_season","sportmonks_football_get_standing_corrections_by_season","sportmonks_football_get_standings","sportmonks_football_get_standings_by_round","sportmonks_football_get_standings_by_season","sportmonks_football_get_state","sportmonks_football_get_states","sportmonks_football_get_team","sportmonks_football_get_team_rankings","sportmonks_football_get_team_rankings_by_date","sportmonks_football_get_team_rankings_by_team","sportmonks_football_get_team_squad","sportmonks_football_get_team_squad_by_season","sportmonks_football_get_teams_by_country","sportmonks_football_get_teams_by_season","sportmonks_football_get_topscorers_by_season","sportmonks_football_get_topscorers_by_stage","sportmonks_football_get_totw","sportmonks_football_get_totw_by_round","sportmonks_football_get_transfer","sportmonks_football_get_transfer_rumour","sportmonks_football_get_transfer_rumours_between_dates","sportmonks_football_get_transfer_rumours_by_player","sportmonks_football_get_transfer_rumours_by_team","sportmonks_football_get_transfers_between_dates","sportmonks_football_get_transfers_by_player","sportmonks_football_get_transfers_by_team","sportmonks_football_get_tv_station","sportmonks_football_get_tv_stations","sportmonks_football_get_tv_stations_by_fixture","sportmonks_football_get_upcoming_fixtures_by_market","sportmonks_football_get_upcoming_fixtures_by_tv_station","sportmonks_football_get_value_bets","sportmonks_football_get_value_bets_by_fixture","sportmonks_football_get_venue","sportmonks_football_get_venues","sportmonks_football_get_venues_by_season","sportmonks_football_search_coaches","sportmonks_football_search_fixtures","sportmonks_football_search_leagues","sportmonks_football_search_players","sportmonks_football_search_referees","sportmonks_football_search_rounds","sportmonks_football_search_seasons","sportmonks_football_search_stages","sportmonks_football_search_teams","sportmonks_football_search_venues","sportmonks_motorsport_get_all_fixtures","sportmonks_motorsport_get_current_leagues_by_team","sportmonks_motorsport_get_driver","sportmonks_motorsport_get_driver_standings","sportmonks_motorsport_get_driver_standings_by_season","sportmonks_motorsport_get_drivers","sportmonks_motorsport_get_drivers_by_country","sportmonks_motorsport_get_drivers_by_season","sportmonks_motorsport_get_fixture","sportmonks_motorsport_get_fixtures_by_date","sportmonks_motorsport_get_fixtures_by_date_range","sportmonks_motorsport_get_fixtures_by_ids","sportmonks_motorsport_get_laps_by_fixture","sportmonks_motorsport_get_laps_by_fixture_and_driver","sportmonks_motorsport_get_laps_by_fixture_and_lap","sportmonks_motorsport_get_latest_laps_by_fixture","sportmonks_motorsport_get_latest_pitstops_by_fixture","sportmonks_motorsport_get_latest_stints_by_fixture","sportmonks_motorsport_get_latest_updated_drivers","sportmonks_motorsport_get_latest_updated_fixtures","sportmonks_motorsport_get_league","sportmonks_motorsport_get_leagues","sportmonks_motorsport_get_leagues_by_country","sportmonks_motorsport_get_leagues_by_date","sportmonks_motorsport_get_leagues_by_live","sportmonks_motorsport_get_leagues_by_team","sportmonks_motorsport_get_livescores","sportmonks_motorsport_get_pitstops_by_fixture","sportmonks_motorsport_get_pitstops_by_fixture_and_driver","sportmonks_motorsport_get_pitstops_by_fixture_and_lap","sportmonks_motorsport_get_race_results_by_season_and_driver","sportmonks_motorsport_get_race_results_by_season_and_team","sportmonks_motorsport_get_schedules_by_season","sportmonks_motorsport_get_season","sportmonks_motorsport_get_seasons","sportmonks_motorsport_get_stage","sportmonks_motorsport_get_stages","sportmonks_motorsport_get_stages_by_season","sportmonks_motorsport_get_state","sportmonks_motorsport_get_states","sportmonks_motorsport_get_stints_by_fixture","sportmonks_motorsport_get_stints_by_fixture_and_driver","sportmonks_motorsport_get_stints_by_fixture_and_stint","sportmonks_motorsport_get_team","sportmonks_motorsport_get_team_standings","sportmonks_motorsport_get_team_standings_by_season","sportmonks_motorsport_get_teams","sportmonks_motorsport_get_teams_by_country","sportmonks_motorsport_get_teams_by_season","sportmonks_motorsport_get_venue","sportmonks_motorsport_get_venues","sportmonks_motorsport_get_venues_by_season","sportmonks_motorsport_search_drivers","sportmonks_motorsport_search_leagues","sportmonks_motorsport_search_stages","sportmonks_motorsport_search_teams","sportmonks_motorsport_search_venues","sportmonks_odds_get_all_historical_odds","sportmonks_odds_get_all_inplay_odds","sportmonks_odds_get_all_pre_match_odds","sportmonks_odds_get_all_premium_odds","sportmonks_odds_get_bookmaker","sportmonks_odds_get_bookmaker_event_ids_by_fixture","sportmonks_odds_get_bookmakers","sportmonks_odds_get_bookmakers_by_fixture","sportmonks_odds_get_inplay_odds_by_fixture","sportmonks_odds_get_inplay_odds_by_fixture_and_bookmaker","sportmonks_odds_get_inplay_odds_by_fixture_and_market","sportmonks_odds_get_last_updated_inplay_odds","sportmonks_odds_get_last_updated_pre_match_odds","sportmonks_odds_get_market","sportmonks_odds_get_markets","sportmonks_odds_get_pre_match_odds_by_fixture","sportmonks_odds_get_pre_match_odds_by_fixture_and_bookmaker","sportmonks_odds_get_pre_match_odds_by_fixture_and_market","sportmonks_odds_get_premium_odds_by_fixture","sportmonks_odds_get_premium_odds_by_fixture_and_bookmaker","sportmonks_odds_get_premium_odds_by_fixture_and_market","sportmonks_odds_get_updated_historical_odds_between","sportmonks_odds_get_updated_premium_odds_between","sportmonks_odds_search_bookmakers","sportmonks_odds_search_markets","spotify_add_playlist_cover","spotify_add_to_queue","spotify_add_tracks_to_playlist","spotify_check_following","spotify_check_playlist_followers","spotify_check_saved_albums","spotify_check_saved_audiobooks","spotify_check_saved_episodes","spotify_check_saved_shows","spotify_check_saved_tracks","spotify_create_playlist","spotify_follow_artists","spotify_follow_playlist","spotify_get_album","spotify_get_album_tracks","spotify_get_albums","spotify_get_artist","spotify_get_artist_albums","spotify_get_artist_top_tracks","spotify_get_artists","spotify_get_audiobook","spotify_get_audiobook_chapters","spotify_get_audiobooks","spotify_get_categories","spotify_get_current_user","spotify_get_currently_playing","spotify_get_devices","spotify_get_episode","spotify_get_episodes","spotify_get_followed_artists","spotify_get_markets","spotify_get_new_releases","spotify_get_playback_state","spotify_get_playlist","spotify_get_playlist_cover","spotify_get_playlist_tracks","spotify_get_queue","spotify_get_recently_played","spotify_get_saved_albums","spotify_get_saved_audiobooks","spotify_get_saved_episodes","spotify_get_saved_shows","spotify_get_saved_tracks","spotify_get_show","spotify_get_show_episodes","spotify_get_shows","spotify_get_top_artists","spotify_get_top_tracks","spotify_get_track","spotify_get_tracks","spotify_get_user_playlists","spotify_get_user_profile","spotify_pause","spotify_play","spotify_remove_saved_albums","spotify_remove_saved_audiobooks","spotify_remove_saved_episodes","spotify_remove_saved_shows","spotify_remove_saved_tracks","spotify_remove_tracks_from_playlist","spotify_reorder_playlist_items","spotify_replace_playlist_items","spotify_save_albums","spotify_save_audiobooks","spotify_save_episodes","spotify_save_shows","spotify_save_tracks","spotify_search","spotify_seek","spotify_set_repeat","spotify_set_shuffle","spotify_set_volume","spotify_skip_next","spotify_skip_previous","spotify_transfer_playback","spotify_unfollow_artists","spotify_unfollow_playlist","spotify_update_playlist","sqs_cancel_message_move_task","sqs_change_message_visibility","sqs_change_message_visibility_batch","sqs_create_queue","sqs_delete_message","sqs_delete_message_batch","sqs_delete_queue","sqs_get_queue_attributes","sqs_get_queue_url","sqs_list_dead_letter_source_queues","sqs_list_message_move_tasks","sqs_list_queue_tags","sqs_list_queues","sqs_purge_queue","sqs_receive_message","sqs_send","sqs_send_message_batch","sqs_set_queue_attributes","sqs_start_message_move_task","sqs_tag_queue","sqs_untag_queue","square_batch_retrieve_inventory_counts","square_cancel_invoice","square_cancel_payment","square_complete_payment","square_create_catalog_image","square_create_customer","square_create_invoice","square_create_order","square_create_payment","square_delete_catalog_object","square_delete_customer","square_delete_invoice","square_get_catalog_object","square_get_customer","square_get_invoice","square_get_location","square_get_order","square_get_payment","square_get_refund","square_list_catalog","square_list_customers","square_list_invoices","square_list_locations","square_list_payments","square_list_refunds","square_pay_order","square_publish_invoice","square_refund_payment","square_search_catalog_objects","square_search_customers","square_search_invoices","square_search_orders","square_update_customer","square_upsert_catalog_object","ssh_check_command_exists","ssh_check_file_exists","ssh_create_directory","ssh_delete_file","ssh_download_file","ssh_download_file_v2","ssh_execute_command","ssh_execute_script","ssh_get_system_info","ssh_list_directory","ssh_move_rename","ssh_read_file_content","ssh_upload_file","ssh_write_file_content","ssm_cancel_command","ssm_delete_parameter","ssm_describe_automation_executions","ssm_describe_instance_information","ssm_describe_instance_patch_states","ssm_describe_instance_patches","ssm_describe_parameters","ssm_get_automation_execution","ssm_get_command_invocation","ssm_get_document","ssm_get_parameter","ssm_get_parameters","ssm_get_parameters_by_path","ssm_list_command_invocations","ssm_list_commands","ssm_list_compliance_items","ssm_list_compliance_summaries","ssm_list_documents","ssm_put_parameter","ssm_send_command","ssm_start_automation_execution","ssm_stop_automation_execution","stagehand_agent","stagehand_extract","stripe_cancel_payment_intent","stripe_cancel_subscription","stripe_capture_charge","stripe_capture_payment_intent","stripe_confirm_payment_intent","stripe_create_charge","stripe_create_customer","stripe_create_invoice","stripe_create_payment_intent","stripe_create_price","stripe_create_product","stripe_create_subscription","stripe_delete_customer","stripe_delete_invoice","stripe_delete_product","stripe_finalize_invoice","stripe_list_charges","stripe_list_customers","stripe_list_events","stripe_list_invoices","stripe_list_payment_intents","stripe_list_prices","stripe_list_products","stripe_list_subscriptions","stripe_pay_invoice","stripe_resume_subscription","stripe_retrieve_charge","stripe_retrieve_customer","stripe_retrieve_event","stripe_retrieve_invoice","stripe_retrieve_payment_intent","stripe_retrieve_price","stripe_retrieve_product","stripe_retrieve_subscription","stripe_search_charges","stripe_search_customers","stripe_search_invoices","stripe_search_payment_intents","stripe_search_prices","stripe_search_products","stripe_search_subscriptions","stripe_send_invoice","stripe_update_charge","stripe_update_customer","stripe_update_invoice","stripe_update_payment_intent","stripe_update_price","stripe_update_product","stripe_update_subscription","stripe_void_invoice","sts_assume_role","sts_assume_role_with_saml","sts_assume_role_with_web_identity","sts_get_access_key_info","sts_get_caller_identity","sts_get_session_token","stt_assemblyai","stt_assemblyai_v2","stt_deepgram","stt_deepgram_v2","stt_elevenlabs","stt_elevenlabs_v2","stt_gemini","stt_gemini_v2","stt_whisper","stt_whisper_v2","supabase_count","supabase_delete","supabase_get_row","supabase_insert","supabase_introspect","supabase_invoke_function","supabase_query","supabase_rpc","supabase_storage_copy","supabase_storage_create_bucket","supabase_storage_create_signed_upload_url","supabase_storage_create_signed_url","supabase_storage_delete","supabase_storage_delete_bucket","supabase_storage_download","supabase_storage_empty_bucket","supabase_storage_get_public_url","supabase_storage_list","supabase_storage_list_buckets","supabase_storage_move","supabase_storage_update_bucket","supabase_storage_upload","supabase_text_search","supabase_update","supabase_upsert","supabase_vector_search","table_batch_insert_rows","table_create","table_delete_row","table_delete_rows_by_filter","table_get_row","table_get_schema","table_insert_row","table_list","table_query_rows","table_query_rows_v2","table_update_row","table_update_rows_by_filter","table_upsert_row","tailscale_approve_user","tailscale_authorize_device","tailscale_create_auth_key","tailscale_delete_auth_key","tailscale_delete_device","tailscale_delete_device_attribute","tailscale_delete_user","tailscale_expire_device_key","tailscale_get_acl","tailscale_get_auth_key","tailscale_get_device","tailscale_get_device_attributes","tailscale_get_device_routes","tailscale_get_dns_configuration","tailscale_get_dns_preferences","tailscale_get_dns_searchpaths","tailscale_get_split_dns","tailscale_get_tailnet_settings","tailscale_get_user","tailscale_list_auth_keys","tailscale_list_devices","tailscale_list_dns_nameservers","tailscale_list_users","tailscale_restore_user","tailscale_set_acl","tailscale_set_device_attribute","tailscale_set_device_name","tailscale_set_device_routes","tailscale_set_device_tags","tailscale_set_dns_configuration","tailscale_set_dns_nameservers","tailscale_set_dns_preferences","tailscale_set_dns_searchpaths","tailscale_set_split_dns","tailscale_set_user_role","tailscale_suspend_user","tailscale_update_device_key","tailscale_update_split_dns","tailscale_update_tailnet_settings","tailscale_validate_acl","tavily_crawl","tavily_extract","tavily_map","tavily_search","telegram_copy_message","telegram_delete_message","telegram_edit_message_text","telegram_forward_message","telegram_get_chat","telegram_get_chat_member","telegram_message","telegram_pin_message","telegram_send_animation","telegram_send_audio","telegram_send_chat_action","telegram_send_contact","telegram_send_document","telegram_send_location","telegram_send_photo","telegram_send_poll","telegram_send_video","telegram_set_message_reaction","telegram_unpin_message","temporal_cancel_workflow","temporal_count_workflows","temporal_create_schedule","temporal_delete_schedule","temporal_describe_schedule","temporal_describe_task_queue","temporal_describe_workflow","temporal_get_workflow_history","temporal_list_schedules","temporal_list_workflows","temporal_pause_schedule","temporal_query_workflow","temporal_reset_workflow","temporal_signal_with_start","temporal_signal_workflow","temporal_start_workflow","temporal_terminate_workflow","temporal_trigger_schedule","temporal_unpause_schedule","temporal_update_workflow","textract_analyze_expense","textract_analyze_id","textract_parser","textract_parser_v2","thinking_tool","thrive_add_audience_managers","thrive_add_audience_members","thrive_add_user_tags","thrive_create_assignment","thrive_create_audience","thrive_create_completion","thrive_create_user","thrive_delete_assignment","thrive_delete_audience","thrive_delete_user","thrive_get_activity","thrive_get_assignment","thrive_get_audience","thrive_get_completion","thrive_get_content","thrive_get_cpd_category","thrive_get_cpd_entry","thrive_get_cpd_requirement","thrive_get_enrolment","thrive_get_skill_levels","thrive_get_tag","thrive_get_user_by_id","thrive_get_user_by_ref","thrive_list_assignments","thrive_list_audience_managers","thrive_list_audience_members","thrive_list_audiences","thrive_list_completions","thrive_list_enrolments","thrive_list_tags","thrive_query_activities","thrive_query_content","thrive_query_cpd_categories","thrive_query_cpd_entries","thrive_query_cpd_requirements","thrive_query_cpd_user_summaries","thrive_remove_audience_manager","thrive_remove_audience_member","thrive_remove_user_tags","thrive_replace_audience_managers","thrive_replace_audience_members","thrive_search_users","thrive_suspend_user","thrive_update_assignment","thrive_update_audience","thrive_update_user","thrive_update_user_skills","tiktok_get_post_status","tiktok_get_user","tiktok_list_videos","tiktok_query_videos","tiktok_upload_video_draft","tinybird_append_datasource","tinybird_delete_datasource_rows","tinybird_events","tinybird_get_job","tinybird_query","tinybird_query_pipe","tinybird_truncate_datasource","tinyfish_cancel_run","tinyfish_fetch","tinyfish_get_run","tinyfish_list_profiles","tinyfish_list_runs","tinyfish_list_vault_items","tinyfish_run","tinyfish_run_async","tinyfish_search","trello_add_checklist","trello_add_checklist_item","trello_add_comment","trello_add_label","trello_add_member","trello_create_board","trello_create_card","trello_create_list","trello_delete_card","trello_get_actions","trello_get_board","trello_get_card","trello_list_cards","trello_list_lists","trello_list_members","trello_remove_label","trello_remove_member","trello_search","trello_update_card","trello_update_checklist_item","trello_update_list","trigger_dev_activate_schedule","trigger_dev_add_run_tags","trigger_dev_batch_trigger_task","trigger_dev_cancel_run","trigger_dev_complete_waitpoint_token","trigger_dev_create_env_var","trigger_dev_create_schedule","trigger_dev_create_waitpoint_token","trigger_dev_deactivate_schedule","trigger_dev_delete_env_var","trigger_dev_delete_schedule","trigger_dev_execute_query","trigger_dev_get_batch","trigger_dev_get_batch_results","trigger_dev_get_deployment","trigger_dev_get_env_var","trigger_dev_get_latest_deployment","trigger_dev_get_query_schema","trigger_dev_get_queue","trigger_dev_get_run","trigger_dev_get_run_events","trigger_dev_get_run_result","trigger_dev_get_run_trace","trigger_dev_get_schedule","trigger_dev_get_waitpoint_token","trigger_dev_import_env_vars","trigger_dev_list_deployments","trigger_dev_list_env_vars","trigger_dev_list_queues","trigger_dev_list_runs","trigger_dev_list_schedules","trigger_dev_list_timezones","trigger_dev_list_waitpoint_tokens","trigger_dev_override_queue_concurrency","trigger_dev_pause_queue","trigger_dev_promote_deployment","trigger_dev_replay_run","trigger_dev_reschedule_run","trigger_dev_reset_queue_concurrency","trigger_dev_resume_queue","trigger_dev_trigger_task","trigger_dev_update_env_var","trigger_dev_update_run_metadata","trigger_dev_update_schedule","tts_azure","tts_cartesia","tts_deepgram","tts_elevenlabs","tts_google","tts_openai","tts_playht","twilio_send_sms","twilio_voice_get_recording","twilio_voice_list_calls","twilio_voice_make_call","typeform_create_form","typeform_delete_form","typeform_files","typeform_get_form","typeform_insights","typeform_list_forms","typeform_responses","typeform_update_form","upstash_redis_command","upstash_redis_delete","upstash_redis_exists","upstash_redis_expire","upstash_redis_get","upstash_redis_hget","upstash_redis_hgetall","upstash_redis_hset","upstash_redis_incr","upstash_redis_incrby","upstash_redis_keys","upstash_redis_lpush","upstash_redis_lrange","upstash_redis_set","upstash_redis_setnx","upstash_redis_ttl","uptimerobot_create_alert_contact","uptimerobot_create_maintenance_window","uptimerobot_create_monitor","uptimerobot_create_psp","uptimerobot_delete_alert_contact","uptimerobot_delete_maintenance_window","uptimerobot_delete_monitor","uptimerobot_delete_psp","uptimerobot_get_account","uptimerobot_get_alert_contact","uptimerobot_get_incident","uptimerobot_get_maintenance_window","uptimerobot_get_monitor","uptimerobot_get_psp","uptimerobot_list_alert_contacts","uptimerobot_list_incidents","uptimerobot_list_maintenance_windows","uptimerobot_list_monitors","uptimerobot_list_psps","uptimerobot_pause_monitor","uptimerobot_start_monitor","uptimerobot_update_maintenance_window","uptimerobot_update_monitor","uptimerobot_update_psp","vanta_download_document_file","vanta_get_control","vanta_get_document","vanta_get_framework","vanta_get_person","vanta_get_policy","vanta_get_risk_scenario","vanta_get_test","vanta_get_vendor","vanta_get_vulnerable_asset","vanta_list_control_documents","vanta_list_control_tests","vanta_list_controls","vanta_list_document_uploads","vanta_list_documents","vanta_list_framework_controls","vanta_list_frameworks","vanta_list_monitored_computers","vanta_list_people","vanta_list_policies","vanta_list_risk_scenarios","vanta_list_test_entities","vanta_list_tests","vanta_list_vendors","vanta_list_vulnerabilities","vanta_list_vulnerability_remediations","vanta_list_vulnerable_assets","vanta_submit_document","vanta_upload_document_file","vercel_add_domain","vercel_add_project_domain","vercel_cancel_deployment","vercel_create_alias","vercel_create_check","vercel_create_deployment","vercel_create_dns_record","vercel_create_edge_config","vercel_create_env_var","vercel_create_project","vercel_create_webhook","vercel_delete_alias","vercel_delete_deployment","vercel_delete_dns_record","vercel_delete_domain","vercel_delete_edge_config","vercel_delete_env_var","vercel_delete_project","vercel_delete_webhook","vercel_get_alias","vercel_get_check","vercel_get_deployment","vercel_get_deployment_events","vercel_get_domain","vercel_get_domain_config","vercel_get_edge_config","vercel_get_edge_config_items","vercel_get_env_vars","vercel_get_project","vercel_get_team","vercel_get_user","vercel_get_webhook","vercel_list_aliases","vercel_list_checks","vercel_list_deployment_files","vercel_list_deployments","vercel_list_dns_records","vercel_list_domains","vercel_list_edge_configs","vercel_list_project_domains","vercel_list_projects","vercel_list_team_members","vercel_list_teams","vercel_list_webhooks","vercel_pause_project","vercel_promote_deployment","vercel_remove_project_domain","vercel_rerequest_check","vercel_unpause_project","vercel_update_check","vercel_update_dns_record","vercel_update_edge_config_items","vercel_update_env_var","vercel_update_project","vercel_update_project_domain","vercel_verify_project_domain","video_falai","video_higgsfield","video_luma","video_minimax","video_runway","video_veo","vision_tool","vision_tool_v2","wealthbox_read_contact","wealthbox_read_note","wealthbox_read_task","wealthbox_write_contact","wealthbox_write_note","wealthbox_write_task","webflow_create_item","webflow_delete_item","webflow_get_item","webflow_list_items","webflow_update_item","webhook_request","whatsapp_get_media","whatsapp_mark_read","whatsapp_send_interactive","whatsapp_send_media","whatsapp_send_message","whatsapp_send_reaction","whatsapp_send_template","whatsapp_upload_media","wikipedia_content","wikipedia_random","wikipedia_search","wikipedia_summary","windchill_check_in_document","windchill_check_in_documents","windchill_check_out_document","windchill_check_out_documents","windchill_create_document","windchill_create_documents","windchill_delete_document","windchill_delete_documents","windchill_download_attachment","windchill_download_primary_content","windchill_get_document","windchill_get_document_structure","windchill_get_primary_content","windchill_get_valid_state_transitions","windchill_list_attachments","windchill_list_documents","windchill_revise_document","windchill_revise_documents","windchill_set_lifecycle_state","windchill_undo_check_out_document","windchill_undo_check_out_documents","windchill_update_common_properties","windchill_update_document","windchill_update_document_security_labels","windchill_update_documents","windchill_upload_attachments","windchill_upload_primary_content","wiza_company_enrichment","wiza_get_credits","wiza_individual_reveal","wiza_prospect_search","wordpress_create_category","wordpress_create_comment","wordpress_create_page","wordpress_create_post","wordpress_create_tag","wordpress_delete_category","wordpress_delete_comment","wordpress_delete_media","wordpress_delete_page","wordpress_delete_post","wordpress_delete_tag","wordpress_get_category","wordpress_get_current_user","wordpress_get_media","wordpress_get_page","wordpress_get_post","wordpress_get_tag","wordpress_get_user","wordpress_list_categories","wordpress_list_comments","wordpress_list_media","wordpress_list_pages","wordpress_list_posts","wordpress_list_tags","wordpress_list_users","wordpress_search_content","wordpress_update_category","wordpress_update_comment","wordpress_update_page","wordpress_update_post","wordpress_update_tag","wordpress_upload_media","workday_assign_onboarding","workday_change_job","workday_create_prehire","workday_get_compensation","workday_get_organizations","workday_get_worker","workday_hire_employee","workday_list_workers","workday_terminate_worker","workday_update_worker","workflow_executor","x_create_bookmark","x_create_tweet","x_delete_bookmark","x_delete_tweet","x_get_blocking","x_get_bookmarks","x_get_followers","x_get_following","x_get_liked_tweets","x_get_liking_users","x_get_me","x_get_personalized_trends","x_get_quote_tweets","x_get_retweeted_by","x_get_trends_by_woeid","x_get_tweets_by_ids","x_get_usage","x_get_user_mentions","x_get_user_timeline","x_get_user_tweets","x_hide_reply","x_manage_block","x_manage_follow","x_manage_like","x_manage_mute","x_manage_retweet","x_read","x_search","x_search_tweets","x_search_users","x_user","x_write","youcom_answer","youcom_finance_research","youcom_get_account_balance","youcom_get_contents","youcom_get_research_task","youcom_research","youcom_search","youcom_search_images","youtube_channel_info","youtube_channel_playlists","youtube_channel_videos","youtube_comments","youtube_playlist_items","youtube_search","youtube_trending","youtube_video_categories","youtube_video_details","zendesk_autocomplete_organizations","zendesk_create_organization","zendesk_create_organizations_bulk","zendesk_create_ticket","zendesk_create_tickets_bulk","zendesk_create_user","zendesk_create_users_bulk","zendesk_delete_organization","zendesk_delete_ticket","zendesk_delete_user","zendesk_get_current_user","zendesk_get_organization","zendesk_get_organizations","zendesk_get_ticket","zendesk_get_tickets","zendesk_get_user","zendesk_get_users","zendesk_merge_tickets","zendesk_search","zendesk_search_count","zendesk_search_users","zendesk_update_organization","zendesk_update_ticket","zendesk_update_tickets_bulk","zendesk_update_user","zendesk_update_users_bulk","zep_add_messages","zep_add_user","zep_create_thread","zep_delete_thread","zep_get_context","zep_get_messages","zep_get_threads","zep_get_user","zep_get_user_threads","zerobounce_get_credits","zerobounce_verify_email","zoho_desk_add_comment","zoho_desk_create_account","zoho_desk_create_contact","zoho_desk_create_ticket","zoho_desk_delete_comment","zoho_desk_get_account","zoho_desk_get_agent","zoho_desk_get_attachment","zoho_desk_get_comment","zoho_desk_get_contact","zoho_desk_get_department","zoho_desk_get_thread","zoho_desk_get_ticket","zoho_desk_list_accounts","zoho_desk_list_agents","zoho_desk_list_comments","zoho_desk_list_contacts","zoho_desk_list_departments","zoho_desk_list_organizations","zoho_desk_list_threads","zoho_desk_list_tickets","zoho_desk_move_ticket","zoho_desk_search_accounts","zoho_desk_search_contacts","zoho_desk_search_tickets","zoho_desk_send_reply","zoho_desk_update_account","zoho_desk_update_comment","zoho_desk_update_contact","zoho_desk_update_ticket","zoom_create_meeting","zoom_delete_meeting","zoom_delete_recording","zoom_get_meeting","zoom_get_meeting_invitation","zoom_get_meeting_recordings","zoom_list_meetings","zoom_list_past_participants","zoom_list_recordings","zoom_update_meeting","zoominfo_enrich_companies","zoominfo_enrich_contacts","zoominfo_search_companies","zoominfo_search_contacts","zoominfo_search_intent","zoominfo_search_news"]' ) export default toolIds diff --git a/apps/sim/tools/generated/tool-metadata.ts b/apps/sim/tools/generated/tool-metadata.ts index 7e8fd53d69f..810b0de8a21 100644 --- a/apps/sim/tools/generated/tool-metadata.ts +++ b/apps/sim/tools/generated/tool-metadata.ts @@ -3,7 +3,7 @@ /** Serializable metadata for every built-in tool, keyed by tool id. */ const toolMetadata: Record = JSON.parse( - '{"a2a_cancel_task":{"id":"a2a_cancel_task","name":"A2A Cancel Task","description":"Request cancellation of an in-progress A2A task.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The task ID to cancel"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_get_agent_card":{"id":"a2a_get_agent_card","name":"A2A Get Agent Card","description":"Fetch the Agent Card (discovery document) for an external A2A agent.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_get_task":{"id":"a2a_get_task","name":"A2A Get Task","description":"Retrieve the current state and result of an A2A task.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The task ID to retrieve"},"historyLength":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of history messages to include"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"a2a_send_message":{"id":"a2a_send_message","name":"A2A Send Message","description":"Send a message to an external A2A agent and return its response.","version":"1.0.0","params":{"agentUrl":{"type":"string","required":true,"visibility":"user-only","description":"The A2A agent endpoint URL"},"message":{"type":"string","required":true,"visibility":"user-or-llm","description":"The message text to send"},"data":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional structured JSON data to attach"},"files":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional files to attach"},"taskId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Existing task ID to continue"},"contextId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Conversation context ID to continue"},"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"API key for authentication (if required)"}},"hostedApiKey":"none"},"affinity_batch_update_entity_fields":{"id":"affinity_batch_update_entity_fields","name":"Affinity Batch Update Entity Fields","description":"Write up to 100 non-list field values on one company or person in a single request.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to write the fields on: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"updates":{"type":"json","required":true,"visibility":"user-or-llm","description":"Up to 100 field updates as [{\\"id\\":\\"\\",\\"value\\":{\\"type\\":\\"…\\",\\"data\\":…}}], using the same value shapes as a single field update"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_batch_update_list_entry_fields":{"id":"affinity_batch_update_list_entry_fields","name":"Affinity Batch Update List Entry Fields","description":"Write up to 100 field values on one list row in a single request. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"updates":{"type":"json","required":true,"visibility":"user-or-llm","description":"Up to 100 field updates as [{\\"id\\":\\"\\",\\"value\\":{\\"type\\":\\"…\\",\\"data\\":…}}], using the same value shapes as a single field update"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_list":{"id":"affinity_create_list","name":"Affinity Create List","description":"Create a list. Its type fixes which entities it can hold, and the API key holder becomes its creator and owner.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the new list"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Entity kind the list holds: company, opportunity, or person"},"isPublic":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether everyone in the organization can see the list"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_list_field_dropdown_option":{"id":"affinity_create_list_field_dropdown_option","name":"Affinity Create List Field Dropdown Option","description":"Add a selectable option to a dropdown field on a list. A ranked or status option also needs a rank and a color.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Kind of option to create, matching the field. dropdown takes only a label; ranked-dropdown also requires rank and color; status-dropdown additionally requires a status category. Sending a field the kind does not accept is rejected"},"text":{"type":"string","required":true,"visibility":"user-or-llm","description":"The option label"},"rank":{"type":"number","required":false,"visibility":"user-or-llm","description":"Sort order. Required on a ranked-dropdown or status-dropdown option"},"color":{"type":"string","required":false,"visibility":"user-or-llm","description":"Option color: white, gray, blue, green, purple, orange, or red. Required on a ranked-dropdown or status-dropdown option"},"statusCategory":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pipeline meaning of the option: open, won, lost, or on-hold. Status-dropdown options only"},"winRate":{"type":"number","required":false,"visibility":"user-or-llm","description":"Expected win rate of the status. Status-dropdown options only"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_merge":{"id":"affinity_create_merge","name":"Affinity Create Merge","description":"Fold a duplicate company or person into the record you are keeping. The merge runs asynchronously — poll the returned task to see it finish. Requires the \\"Manage duplicates\\" permission and an admin role.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to merge: companies or persons"},"primaryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to keep"},"duplicateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the duplicate record to fold in"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_note":{"id":"affinity_create_note","name":"Affinity Create Note","description":"Write a note — attached to companies, persons, and opportunities, anchored to a meeting, call, or chat message, or posted as a reply to an existing note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Note shape: entities to attach it to records, interaction to anchor it to a meeting, call, or chat message, or user-reply to reply to a note"},"html":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note body as HTML"},"companyIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Companies to attach the note to, e.g. [1, 2]. Not used on a reply"},"personIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Persons to attach the note to, e.g. [1, 2]. Not used on a reply"},"opportunityIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Opportunities to attach the note to, e.g. [1, 2]. Not used on a reply"},"interactionId":{"type":"string","required":false,"visibility":"user-or-llm","description":"The interaction to anchor the note to. Required for an interaction note"},"interactionType":{"type":"string","required":false,"visibility":"user-or-llm","description":"Kind of the anchoring interaction: meeting, call, or chat-message. Required for an interaction note"},"parentId":{"type":"string","required":false,"visibility":"user-or-llm","description":"The note being replied to. Required for a user-reply note"},"creatorId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Attribute the note to another internal person. Defaults to the API key holder"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"Backdate the note to this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_create_reminder":{"id":"affinity_create_reminder","name":"Affinity Create Reminder","description":"Create a reminder on one company, person, or opportunity. A recurring reminder resets whenever the chosen signal happens instead of firing once.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"one-time to fire once, or recurring to reset on a signal"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"What the reminder is about: company, person, or opportunity"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company, person, or opportunity"},"dueDate":{"type":"string","required":false,"visibility":"user-or-llm","description":"When the reminder is due, as an ISO 8601 timestamp. Required for a one-time reminder; on a recurring one Affinity computes it from the period when omitted"},"content":{"type":"string","required":false,"visibility":"user-or-llm","description":"What the reminder says"},"ownerId":{"type":"string","required":true,"visibility":"user-or-llm","description":"User the reminder is assigned to. Must be an internal user. The API key holder is recorded as the creator, which is a separate field"},"resetTrigger":{"type":"string","required":false,"visibility":"user-or-llm","description":"What restarts a recurring reminder: interaction, email, or event. Required when the type is recurring"},"periodDays":{"type":"number","required":false,"visibility":"user-or-llm","description":"Days between firings of a recurring reminder. Required when the type is recurring"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_delete_list_field_dropdown_option":{"id":"affinity_delete_list_field_dropdown_option","name":"Affinity Delete List Field Dropdown Option","description":"Permanently delete a dropdown option on a list field. Every list entry currently set to it is cleared, and those values cannot be recovered.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID to delete"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_delete_note":{"id":"affinity_delete_note","name":"Affinity Delete Note","description":"Delete a note you created. Deleting a root note also deletes its replies; deleting a reply removes only that reply.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID to delete"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_company":{"id":"affinity_get_company","name":"Affinity Get Company","description":"Look up one company by ID. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"companyId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The company ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_current_user":{"id":"affinity_get_current_user","name":"Affinity Get Current User","description":"Verify an Affinity API key and return the tenant, the user behind the key, and the scopes the grant carries.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_entity_field_value":{"id":"affinity_get_entity_field_value","name":"Affinity Get Entity Field Value","description":"Read one non-list field value from a company or person.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to read the field from: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to read"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list":{"id":"affinity_get_list","name":"Affinity Get List","description":"Read one list — its name, type, owner, and privacy setting.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_entry":{"id":"affinity_get_list_entry","name":"Affinity Get List Entry","description":"Read one row of a list with its entity. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_entry_field":{"id":"affinity_get_list_entry_field","name":"Affinity Get List Entry Field","description":"Read one field value on a list row.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to read"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_list_field_dropdown_option":{"id":"affinity_get_list_field_dropdown_option","name":"Affinity Get List Field Dropdown Option","description":"Read one dropdown option on a list field.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_merge":{"id":"affinity_get_merge","name":"Affinity Get Merge","description":"Read the status of one company or person merge, including why it failed if it did.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge to read: companies or persons"},"mergeId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The merge ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_merge_task":{"id":"affinity_get_merge_task","name":"Affinity Get Merge Task","description":"Read one merge task and how its merges are progressing. Poll this after starting a merge.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge task to read: companies or persons"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The merge task ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_note":{"id":"affinity_get_note","name":"Affinity Get Note","description":"Read one note with its body, author, mentions, and attached records.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return, e.g. [\\"repliesCount\\",\\"personsPreview\\",\\"companiesPreview\\",\\"opportunitiesPreview\\"]. Those four fields are omitted unless requested here"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_opportunity":{"id":"affinity_get_opportunity","name":"Affinity Get Opportunity","description":"Read one opportunity and the list it belongs to. Its field data lives on the list entry.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"opportunityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The opportunity ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_person":{"id":"affinity_get_person","name":"Affinity Get Person","description":"Look up one person by ID. Field data is returned only for the Field IDs or Field Types asked for.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"personId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The person ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_saved_view":{"id":"affinity_get_saved_view","name":"Affinity Get Saved View","description":"Read one saved view — its name, kind, and creation date.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"viewId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The saved view ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_transcript":{"id":"affinity_get_transcript","name":"Affinity Get Transcript","description":"Read one transcript with its first 100 fragments. Page the fragments endpoint for a longer meeting.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"transcriptId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The transcript ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_get_user":{"id":"affinity_get_user","name":"Affinity Get User","description":"Read one internal user. A user and their person record share the same numeric ID, so a person ID works here.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"userId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The user ID, which is also their person ID"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_calls":{"id":"affinity_list_calls","name":"Affinity List Calls","description":"Page through logged calls and their participants. Only calls the API key holder can see are returned.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_chat_messages":{"id":"affinity_list_chat_messages","name":"Affinity List Chat Messages","description":"Page through logged chat messages and their participants. Only messages the API key holder can see are returned.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_companies":{"id":"affinity_list_companies","name":"Affinity List Companies","description":"Page through companies. Companies come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these company IDs, e.g. [1, 2, 3]"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_coworker_connections":{"id":"affinity_list_coworker_connections","name":"Affinity List Coworker Connections","description":"Find warm paths into a company through shared work history: who in your Affinity data once worked alongside the people you want to reach. Grouped by target, strongest first.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":true,"visibility":"user-or-llm","description":"Required scope. The only supported filter is target.currentCompany.id, e.g. \\"target.currentCompany.id=123\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of targets to return per page, 1-50. Defaults to 20"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_emails":{"id":"affinity_list_emails","name":"Affinity List Emails","description":"Page through email metadata — subject, participants, and timestamps. Affinity never exposes email bodies through the API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_field_values":{"id":"affinity_list_entity_field_values","name":"Affinity List Entity Field Values","description":"Page through a company\'s or person\'s non-list field values. List fields are not returned here — read those through the list entry.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to read field values from: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field IDs. Mutually exclusive with Field Types"},"types":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field categories: enriched, global, relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_list_entries":{"id":"affinity_list_entity_list_entries","name":"Affinity List Entity List Entries","description":"Page through a company\'s or person\'s rows across every list, each carrying that list\'s field values and when the entity was added.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up the rows of: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_lists":{"id":"affinity_list_entity_lists","name":"Affinity List Entity Lists","description":"List every list a company or person appears on that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up the lists of: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_notes":{"id":"affinity_list_entity_notes","name":"Affinity List Entity Notes","description":"List the notes relevant to one company, person, or opportunity — directly attached notes plus notes reaching it through its people and meetings.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity the notes hang off: companies, persons, or opportunities"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company, person, or opportunity"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_entity_relationships":{"id":"affinity_list_entity_relationships","name":"Affinity List Entity Relationships","description":"List who knows a company or person, scored 0.0 to 1.0 by how much the two actually interact. Strongest first by default.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to look up relationships for: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on interactionScore only, e.g. \\"interactionScore>=0.5\\""},"orderBy":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order: [\\"interactionScore\\"] for weakest first, [\\"-interactionScore\\"] for strongest first (the default)"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_dropdown_options":{"id":"affinity_list_field_dropdown_options","name":"Affinity List Field Dropdown Options","description":"List the selectable options on a dropdown or ranked-dropdown company or person field. Writing such a field needs the option ID, not its text.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which field family the field belongs to: companies or persons"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown or ranked-dropdown field ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_metadata":{"id":"affinity_list_field_metadata","name":"Affinity List Field Metadata","description":"List the non-list company or person fields, with the value type, filter operators, and sort support of each. Start here to find the Field IDs the read and write tools take.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which fields to describe: companies or persons"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return: [\\"filterability\\",\\"sortability\\"]. Both are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on name only, e.g. \\"name=~Status\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_field_value_changes":{"id":"affinity_list_field_value_changes","name":"Affinity List Field Value Changes","description":"Page through field value changes across the whole workspace. Built for delta sync: follow nextCursor to the end of a run, then resume from the last cursor next time.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over field.id, listEntry.id, changer.id, changedAt, or actionType. Resume a sync with e.g. \\"changedAt>2026-06-01T12:00:00Z\\""},"orderBy":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order: [\\"changedAt\\"] for oldest first (the default), [\\"-changedAt\\"] for newest first"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_investor_executive_connections":{"id":"affinity_list_investor_executive_connections","name":"Affinity List Investor Executive Connections","description":"Find warm paths into a company through investment history: which investors in your Affinity data backed a company the people you want to reach once led. Grouped by target, strongest first.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":true,"visibility":"user-or-llm","description":"Required scope. The only supported filter is target.currentCompany.id, e.g. \\"target.currentCompany.id=123\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of targets to return per page, 1-50. Defaults to 20"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entries":{"id":"affinity_list_list_entries","name":"Affinity List List Entries","description":"Page through the rows of a list. Rows come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entry_field_value_changes":{"id":"affinity_list_list_entry_field_value_changes","name":"Affinity List List Entry Field Value Changes","description":"Page through the history of one list row — who changed which field, when, and to what.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over field.id, changer.id, changedAt, or actionType, e.g. \\"field.id=field-1234\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_entry_fields":{"id":"affinity_list_list_entry_fields","name":"Affinity List List Entry Fields","description":"Page through every field value on one list row, including the list-specific columns. All fields are returned unless narrowed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field IDs. Mutually exclusive with Field Types"},"types":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict to these field categories: enriched, global, list, relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_field_dropdown_options":{"id":"affinity_list_list_field_dropdown_options","name":"Affinity List List Field Dropdown Options","description":"List the selectable options on a dropdown, ranked-dropdown, or status-dropdown field of a list.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_list_fields":{"id":"affinity_list_list_fields","name":"Affinity List List Fields","description":"List the fields available on one list, including its list-specific columns. Use these Field IDs when reading or writing list entries.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return: [\\"filterability\\",\\"sortability\\"]. Both are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on name only, e.g. \\"name=~Stage\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_lists":{"id":"affinity_list_lists","name":"Affinity List Lists","description":"Page through the lists in the organization that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"term":{"type":"string","required":false,"visibility":"user-or-llm","description":"Case-insensitive substring match on the list name"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_meetings":{"id":"affinity_list_meetings","name":"Affinity List Meetings","description":"Page through past and upcoming meetings with their organizer and attendees.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_merge_tasks":{"id":"affinity_list_merge_tasks","name":"Affinity List Merge Tasks","description":"Page through merge tasks, each summarizing how many of its merges are in progress, succeeded, or failed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merge tasks to list: companies or persons"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression. This endpoint filters on status only, e.g. \\"status=in-progress\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_merges":{"id":"affinity_list_merges","name":"Affinity List Merges","description":"Page through the company or person merges the organization has run, with the status and the records involved in each.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which merges to list: companies or persons"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over status or taskId, e.g. \\"status=failed\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_companies":{"id":"affinity_list_note_attached_companies","name":"Affinity List Note Attached Companies","description":"List the companies directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_opportunities":{"id":"affinity_list_note_attached_opportunities","name":"Affinity List Note Attached Opportunities","description":"List the opportunities directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_attached_persons":{"id":"affinity_list_note_attached_persons","name":"Affinity List Note Attached Persons","description":"List the persons directly attached to one note.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_note_replies":{"id":"affinity_list_note_replies","name":"Affinity List Note Replies","description":"Page through the replies on one note, including AI Notetaker replies.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID whose replies to read"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_notes":{"id":"affinity_list_notes","name":"Affinity List Notes","description":"Page through every note the caller can see. Replies are excluded.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"includes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Extra properties to return, e.g. [\\"repliesCount\\",\\"personsPreview\\",\\"companiesPreview\\",\\"opportunitiesPreview\\"]. Those four fields are omitted unless requested here"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_opportunities":{"id":"affinity_list_opportunities","name":"Affinity List Opportunities","description":"Page through opportunities. Field data lives on the list entry, not here — read it through the list or saved view the opportunity belongs to.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these opportunity IDs, e.g. [1, 2, 3]"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_persons":{"id":"affinity_list_persons","name":"Affinity List Persons","description":"Page through persons. Persons come back without field data unless Field IDs or Field Types asks for it.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the page to these person IDs, e.g. [1, 2, 3]"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_reminders":{"id":"affinity_list_reminders","name":"Affinity List Reminders","description":"Page through the reminders the caller can see. Filter by status to surface what is overdue.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_saved_view_entries":{"id":"affinity_list_saved_view_entries","name":"Affinity List Saved View Entries","description":"Page through the rows of a saved view. The view\'s own filters and columns decide which rows and which field data come back.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"viewId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The saved view ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_saved_views":{"id":"affinity_list_saved_views","name":"Affinity List Saved Views","description":"List the saved views on a list that the caller can view.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_transcript_fragments":{"id":"affinity_list_transcript_fragments","name":"Affinity List Transcript Fragments","description":"Page through everything said in a meeting, segment by segment with the speaker.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"transcriptId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The transcript ID"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_transcripts":{"id":"affinity_list_transcripts","name":"Affinity List Transcripts","description":"Page through meeting transcript metadata. Read one transcript to get what was actually said.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression, e.g. \\"createdAt>=2026-01-01\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_list_users":{"id":"affinity_list_users","name":"Affinity List Users","description":"Page through the internal users in the organization. Email addresses and roles are returned only to callers with the \\"Manage Users\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"term":{"type":"string","required":false,"visibility":"user-or-llm","description":"Case-insensitive match across first name, last name, and primary email"},"filter":{"type":"string","required":false,"visibility":"user-or-llm","description":"Affinity Filtering Language expression over id or status, e.g. \\"status=active\\""},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_companies":{"id":"affinity_search_companies","name":"Affinity Search Companies","description":"Search companies by filters, sorts, and a free-text term. Requires the \\"Export All Organizations directory\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_files":{"id":"affinity_search_files","name":"Affinity Search Files","description":"Search files by keyword, ordered by relevance. Narrow to specific files or to one company, or leave both unset to search the whole account.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to search for. Between 3 and 500 characters"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to these file IDs. Cannot be combined with Company ID"},"companyId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Restrict the search to one company\'s files. Cannot be combined with file IDs"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of files to return, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_list_entries":{"id":"affinity_search_list_entries","name":"Affinity Search List Entries","description":"Search the rows of one list by filters, sorts, and a free-text term. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID to search"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, list, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_notes":{"id":"affinity_search_notes","name":"Affinity Search Notes","description":"Search notes by keyword, ordered by relevance. Narrow to specific notes or to one company, or leave both unset to search the whole account.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to search for. Between 3 and 500 characters"},"ids":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to these note IDs. Cannot be combined with Company ID"},"companyId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Restrict the search to one company\'s notes. Cannot be combined with note IDs"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of notes to return, 1-100. Defaults to 20"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_search_persons":{"id":"affinity_search_persons","name":"Affinity Search Persons","description":"Search persons by filters, sorts, and a free-text term. Requires the \\"Export All People directory\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"filters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Filter group as {operator: \\"and\\"|\\"or\\", filters: [...]}, at most 50 leaves. Each leaf is {valueType, fieldId, operator, value}, and a leaf may itself be a nested group"},"searchTerm":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-text term matched against the searchable fields. At least 3 characters"},"searchFieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs the search term is matched against. Defaults to the searchable fields"},"sorts":{"type":"json","required":false,"visibility":"user-or-llm","description":"Sort order as [{fieldId, direction: \\"asc\\"|\\"desc\\", attributeId?}], up to 5, applied in order"},"fieldIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field IDs to return values for, e.g. [\\"affinity-data-location\\"]. Mutually exclusive with Field Types"},"fieldTypes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Field categories to return values for: enriched, global, or relationship-intelligence. Mutually exclusive with Field IDs"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous page, returned as nextCursor or prevCursor"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to return per page, 1-100. Defaults to 100"},"totalCount":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the total size of the collection. Costs an extra query"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_semantic_search":{"id":"affinity_semantic_search","name":"Affinity Semantic Search","description":"Find companies from a description in plain language — industry, technology, stage, or business model. Currently searches companies only.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"prompt":{"type":"string","required":true,"visibility":"user-or-llm","description":"What to look for, in plain language, e.g. \\"climate tech companies in our pipeline\\". Up to 500 characters"},"listIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Restrict the search to companies on these lists, e.g. [1, 2]"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of companies to return, 1-100. Defaults to 100"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_entity_field_value":{"id":"affinity_update_entity_field_value","name":"Affinity Update Entity Field Value","description":"Write one non-list field value on a company or person. The value type must match how the field is defined.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"entityType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Which entity to write the field on: companies or persons"},"entityId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of that company or person"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to write"},"value":{"type":"json","required":true,"visibility":"user-or-llm","description":"The new value as {type, data}, where type matches the field\'s value type. Examples: {\\"type\\":\\"text\\",\\"data\\":\\"Series B\\"}, {\\"type\\":\\"number\\",\\"data\\":42}, {\\"type\\":\\"dropdown\\",\\"data\\":{\\"dropdownOptionId\\":7}}, {\\"type\\":\\"person\\",\\"data\\":{\\"id\\":123}}, {\\"type\\":\\"person-multi\\",\\"data\\":[{\\"id\\":123}]}. Pass data as null to clear the field"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_list_entry_field":{"id":"affinity_update_list_entry_field","name":"Affinity Update List Entry Field","description":"Write one field value on a list row. Requires the \\"Export data from Lists\\" permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"listEntryId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list entry ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The field ID to write"},"value":{"type":"json","required":true,"visibility":"user-or-llm","description":"The new value as {type, data}, where type matches the field\'s value type. Examples: {\\"type\\":\\"text\\",\\"data\\":\\"Series B\\"}, {\\"type\\":\\"number\\",\\"data\\":42}, {\\"type\\":\\"dropdown\\",\\"data\\":{\\"dropdownOptionId\\":7}}, {\\"type\\":\\"person\\",\\"data\\":{\\"id\\":123}}, {\\"type\\":\\"person-multi\\",\\"data\\":[{\\"id\\":123}]}. Pass data as null to clear the field"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_list_field_dropdown_option":{"id":"affinity_update_list_field_dropdown_option","name":"Affinity Update List Field Dropdown Option","description":"Change a dropdown option on a list field. Every field is optional — supply only what should change, and only fields the option\'s kind actually has.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"listId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The list ID"},"fieldId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown field ID on that list"},"dropdownOptionId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The dropdown option ID to update"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Replacement option label. Supply at least one field to change"},"rank":{"type":"number","required":false,"visibility":"user-or-llm","description":"Sort order. Required on a ranked-dropdown or status-dropdown option"},"color":{"type":"string","required":false,"visibility":"user-or-llm","description":"Option color: white, gray, blue, green, purple, orange, or red. Required on a ranked-dropdown or status-dropdown option"},"statusCategory":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pipeline meaning of the option: open, won, lost, or on-hold. Status-dropdown options only"},"winRate":{"type":"number","required":false,"visibility":"user-or-llm","description":"Expected win rate of the status. Status-dropdown options only"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"affinity_update_note":{"id":"affinity_update_note","name":"Affinity Update Note","description":"Rewrite a note\'s body or replace which records it is attached to. Each list of IDs replaces that association wholesale, an empty list clears it, and omitting one leaves it untouched. A note\'s type cannot be changed.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Affinity API key, sent as a bearer token"},"noteId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note ID to update"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"Replacement note body as HTML"},"companyIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached companies, e.g. [1, 2]. Send [] to detach every company; omit to leave them unchanged"},"personIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached persons, e.g. [1, 2]. Send [] to detach every person; omit to leave them unchanged"},"opportunityIds":{"type":"json","required":false,"visibility":"user-or-llm","description":"Replacement set of attached opportunities, e.g. [1, 2]. Send [] to detach every opportunity; omit to leave them unchanged"}},"oauth":{"required":false,"provider":"affinity","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_create_draft":{"id":"agentmail_create_draft","name":"Create Draft","description":"Create a new email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to create the draft in"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Draft subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text draft body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML draft body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"},"inReplyTo":{"type":"string","required":false,"visibility":"user-or-llm","description":"ID of message being replied to"},"sendAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to schedule sending"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_create_inbox":{"id":"agentmail_create_inbox","name":"Create Inbox","description":"Create a new email inbox with AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"username":{"type":"string","required":false,"visibility":"user-or-llm","description":"Username for the inbox email address"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Domain for the inbox email address"},"displayName":{"type":"string","required":false,"visibility":"user-or-llm","description":"Display name for the inbox"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_draft":{"id":"agentmail_delete_draft","name":"Delete Draft","description":"Delete an email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to delete"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_inbox":{"id":"agentmail_delete_inbox","name":"Delete Inbox","description":"Delete an email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to delete"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_delete_thread":{"id":"agentmail_delete_thread","name":"Delete Thread","description":"Delete an email thread in AgentMail (moves to trash, or permanently deletes if already in trash)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to delete"},"permanent":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Force permanent deletion instead of moving to trash"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_forward_message":{"id":"agentmail_forward_message","name":"Forward Message","description":"Forward an email message to new recipients in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to forward"},"to":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Override subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Additional plain text to prepend"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"Additional HTML to prepend"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_draft":{"id":"agentmail_get_draft","name":"Get Draft","description":"Get details of a specific email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox the draft belongs to"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_inbox":{"id":"agentmail_get_inbox","name":"Get Inbox","description":"Get details of a specific email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_message":{"id":"agentmail_get_message","name":"Get Message","description":"Get details of a specific email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_get_thread":{"id":"agentmail_get_thread","name":"Get Thread","description":"Get details of a specific email thread including messages in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to retrieve"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_drafts":{"id":"agentmail_list_drafts","name":"List Drafts","description":"List email drafts in an inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list drafts from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of drafts to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_inboxes":{"id":"agentmail_list_inboxes","name":"List Inboxes","description":"List all email inboxes in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of inboxes to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_messages":{"id":"agentmail_list_messages","name":"List Messages","description":"List messages in an inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list messages from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of messages to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_list_threads":{"id":"agentmail_list_threads","name":"List Threads","description":"List email threads in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to list threads from"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of threads to return"},"pageToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token for next page of results"},"labels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to filter threads by"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter threads before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter threads after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_reply_message":{"id":"agentmail_reply_message","name":"Reply to Message","description":"Reply to an existing email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to reply from"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to reply to"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text reply body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML reply body"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Override recipient email addresses (comma-separated)"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC email addresses (comma-separated)"},"replyAll":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Reply to all recipients of the original message"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_send_draft":{"id":"agentmail_send_draft","name":"Send Draft","description":"Send an existing email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to send"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_send_message":{"id":"agentmail_send_message","name":"Send Message","description":"Send an email message from an AgentMail inbox","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to send from"},"to":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient email address (comma-separated for multiple)"},"subject":{"type":"string","required":true,"visibility":"user-or-llm","description":"Email subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text email body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML email body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_draft":{"id":"agentmail_update_draft","name":"Update Draft","description":"Update an existing email draft in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the draft"},"draftId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the draft to update"},"to":{"type":"string","required":false,"visibility":"user-or-llm","description":"Recipient email addresses (comma-separated)"},"subject":{"type":"string","required":false,"visibility":"user-or-llm","description":"Draft subject line"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Plain text draft body"},"html":{"type":"string","required":false,"visibility":"user-or-llm","description":"HTML draft body"},"cc":{"type":"string","required":false,"visibility":"user-or-llm","description":"CC recipient email addresses (comma-separated)"},"bcc":{"type":"string","required":false,"visibility":"user-or-llm","description":"BCC recipient email addresses (comma-separated)"},"sendAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to schedule sending"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_inbox":{"id":"agentmail_update_inbox","name":"Update Inbox","description":"Update the display name of an email inbox in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox to update"},"displayName":{"type":"string","required":true,"visibility":"user-or-llm","description":"New display name for the inbox"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_message":{"id":"agentmail_update_message","name":"Update Message","description":"Add or remove labels on an email message in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the message"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to update"},"addLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to add to the message"},"removeLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to remove from the message"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentmail_update_thread":{"id":"agentmail_update_thread","name":"Update Thread Labels","description":"Add or remove labels on an email thread in AgentMail","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentMail API key"},"inboxId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the inbox containing the thread"},"threadId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the thread to update"},"addLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to add to the thread"},"removeLabels":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated labels to remove from the thread"}},"oauth":{"required":false,"provider":"agentmail","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_call":{"id":"agentphone_create_call","name":"Create Outbound Call","description":"Initiate an outbound voice call from an AgentPhone agent","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"agentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Agent that will handle the call"},"toNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Phone number to call in E.164 format (e.g. +14155551234)"},"fromNumberId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Phone number ID to use as caller ID. Must belong to the agent. If omitted, the agent\'s first assigned number is used."},"initialGreeting":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optional greeting spoken when the recipient answers"},"voice":{"type":"string","required":false,"visibility":"user-or-llm","description":"Voice ID override for this call (defaults to the agent\'s configured voice)"},"systemPrompt":{"type":"string","required":false,"visibility":"user-or-llm","description":"When provided, uses a built-in LLM for the conversation instead of forwarding to your webhook"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_contact":{"id":"agentphone_create_contact","name":"Create Contact","description":"Create a new contact in AgentPhone","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"phoneNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Phone number in E.164 format (e.g. +14155551234)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact\'s full name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Contact\'s email address"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Freeform notes stored on the contact"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_create_number":{"id":"agentphone_create_number","name":"Create Phone Number","description":"Provision a new SMS- and voice-enabled phone number","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Two-letter country code (e.g. US, CA). Defaults to US."},"areaCode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Preferred area code (US/CA only, e.g. \\"415\\"). Best-effort — may be ignored if unavailable."},"agentId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optionally attach the number to an agent immediately"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_delete_contact":{"id":"agentphone_delete_contact","name":"Delete Contact","description":"Delete a contact by ID","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_call":{"id":"agentphone_get_call","name":"Get Call","description":"Fetch a call and its full transcript","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"callId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the call to retrieve"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_call_transcript":{"id":"agentphone_get_call_transcript","name":"Get Call Transcript","description":"Get the full ordered transcript for a call","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"callId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the call to retrieve the transcript for"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_contact":{"id":"agentphone_get_contact","name":"Get Contact","description":"Fetch a single contact by ID","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_conversation":{"id":"agentphone_get_conversation","name":"Get Conversation","description":"Get a conversation along with its recent messages","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"messageLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of recent messages to include (default 50, max 100)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_conversation_messages":{"id":"agentphone_get_conversation_messages","name":"Get Conversation Messages","description":"Get paginated messages for a conversation","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of messages to return (default 50, max 200)"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_number_messages":{"id":"agentphone_get_number_messages","name":"Get Phone Number Messages","description":"Fetch messages received on a specific phone number","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"numberId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the phone number"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of messages to return (default 50, max 200)"},"before":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received before this ISO 8601 timestamp"},"after":{"type":"string","required":false,"visibility":"user-or-llm","description":"Return messages received after this ISO 8601 timestamp"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage":{"id":"agentphone_get_usage","name":"Get Usage","description":"Retrieve current usage statistics for the AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage_daily":{"id":"agentphone_get_usage_daily","name":"Get Daily Usage","description":"Get a daily breakdown of usage (messages, calls, webhooks) for the last N days","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"days":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of days to return (1-365, default 30)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_get_usage_monthly":{"id":"agentphone_get_usage_monthly","name":"Get Monthly Usage","description":"Get monthly usage aggregation (messages, calls, webhooks) for the last N months","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"months":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of months to return (1-24, default 6)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_calls":{"id":"agentphone_list_calls","name":"List Calls","description":"List voice calls for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"},"status":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by status (completed, in-progress, failed)"},"direction":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by direction (inbound, outbound)"},"type":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by call type (pstn, web)"},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search by phone number (matches fromNumber or toNumber)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_contacts":{"id":"agentphone_list_contacts","name":"List Contacts","description":"List contacts for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter by name or phone number (case-insensitive contains)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 50, max 200)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_conversations":{"id":"agentphone_list_conversations","name":"List Conversations","description":"List conversations (message threads) for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_list_numbers":{"id":"agentphone_list_numbers","name":"List Phone Numbers","description":"List all phone numbers provisioned for this AgentPhone account","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to return (default 20, max 100)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip (min 0)"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_react_to_message":{"id":"agentphone_react_to_message","name":"React to Message","description":"Send an iMessage tapback reaction to a message (iMessage only)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"messageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the message to react to"},"reaction":{"type":"string","required":true,"visibility":"user-or-llm","description":"Reaction type: love, like, dislike, laugh, emphasize, or question"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_release_number":{"id":"agentphone_release_number","name":"Release Phone Number","description":"Release (delete) a phone number. This action is irreversible.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"numberId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the phone number to release"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_send_message":{"id":"agentphone_send_message","name":"Send Message","description":"Send an outbound SMS or iMessage from an AgentPhone agent","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"agentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Agent sending the message"},"toNumber":{"type":"string","required":true,"visibility":"user-or-llm","description":"Recipient phone number in E.164 format (e.g. +14155551234)"},"body":{"type":"string","required":true,"visibility":"user-or-llm","description":"Message text to send"},"mediaUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"Optional URL of an image, video, or file to attach"},"numberId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Phone number ID to send from. If omitted, the agent\'s first assigned number is used."}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_update_contact":{"id":"agentphone_update_contact","name":"Update Contact","description":"Update a contact\'s fields","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"contactId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Contact ID"},"phoneNumber":{"type":"string","required":false,"visibility":"user-or-llm","description":"New phone number in E.164 format"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New contact name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"New email address"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"New freeform notes"}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agentphone_update_conversation":{"id":"agentphone_update_conversation","name":"Update Conversation","description":"Update conversation metadata (stored state). Pass null to clear existing metadata.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"AgentPhone API key"},"conversationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Conversation ID"},"metadata":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom key-value metadata to store on the conversation. Pass null to clear existing metadata."}},"oauth":{"required":false,"provider":"agentphone","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"agiloft_async_status":{"id":"agiloft_async_status","name":"Agiloft Async Status","description":"Check whether an asynchronous Agiloft call, such as a run action button, has completed.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table the asynchronous call was made against"},"callbackId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Callback ID returned by the asynchronous call, e.g. from Run Action Button"}},"hostedApiKey":"none"},"agiloft_attach_file":{"id":"agiloft_attach_file","name":"Agiloft Attach File","description":"Attach a file to a field in an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to attach the file to"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"file":{"type":"file","required":true,"visibility":"user-or-llm","description":"File to attach"},"fileName":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name to assign to the file (defaults to original file name)"},"overwrite":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Replace the contents of the field instead of adding another file to it"}},"hostedApiKey":"none"},"agiloft_attachment_info":{"id":"agiloft_attachment_info","name":"Agiloft Attachment Info","description":"Get information about file attachments on a record field.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to check attachments on"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field to inspect"}},"hostedApiKey":"none"},"agiloft_create_record":{"id":"agiloft_create_record","name":"Agiloft Create Record","description":"Create a new record in an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record field values as a JSON object (e.g., {\\"first_name\\": \\"John\\", \\"status\\": \\"Active\\"})"}},"hostedApiKey":"none"},"agiloft_delete_record":{"id":"agiloft_delete_record","name":"Agiloft Delete Record","description":"Delete a record from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to delete"},"substituteIds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated IDs of records that adopt the dependants of the deleted record. Read only when the delete rule is REPLACE_WITH_ANOTHER."},"deleteRule":{"type":"string","required":false,"visibility":"user-or-llm","description":"How to treat records that depend on this one: ERROR_IF_DEPENDANTS (default — fails rather than cascading), APPLY_DELETE_WHERE_POSSIBLE, DELETE_WHERE_POSSIBLE_OTHERWISE_UNLINK, APPLY_UNLINK, UNLINK_WHERE_POSSIBLE_OTHERWISE_DELETE, or REPLACE_WITH_ANOTHER"}},"hostedApiKey":"none"},"agiloft_get_choice_line_id":{"id":"agiloft_get_choice_line_id","name":"Agiloft Get Choice Line ID","description":"Resolve the internal numeric ID of a choice-list value, for use in EWSelect WHERE clauses against choice fields.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"case\\", \\"contracts\\")"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Choice field name (e.g., \\"priority\\", \\"status\\")"},"value":{"type":"string","required":true,"visibility":"user-or-llm","description":"Choice display value to resolve (e.g., \\"High\\", \\"Active\\")"}},"hostedApiKey":"none"},"agiloft_list_tables":{"id":"agiloft_list_tables","name":"Agiloft List Tables","description":"List the tables and fields in an Agiloft knowledge base, to discover the logical names other operations need.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":false,"visibility":"user-or-llm","description":"Logical name of a single table to describe (e.g., \\"contacts\\"). Leave empty to list every table in the knowledge base."},"includeLinkedInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Include the source table and column behind each linked field"},"skipColumnsInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Return table names only, omitting field details, for a much smaller response"}},"hostedApiKey":"none"},"agiloft_lock_record":{"id":"agiloft_lock_record","name":"Agiloft Lock Record","description":"Lock, unlock, or check the lock status of an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to lock, unlock, or check"},"lockAction":{"type":"string","required":true,"visibility":"user-or-llm","description":"Action to perform: \\"lock\\", \\"unlock\\", or \\"check\\""},"force":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Unlock only: release a lock held by another user."}},"hostedApiKey":"none"},"agiloft_nlp_search":{"id":"agiloft_nlp_search","name":"Agiloft Natural Language Search","description":"Search Agiloft records by describing what you want in plain language, such as \\"active NDAs submitted last month\\".","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"nlpQuery":{"type":"string","required":true,"visibility":"user-or-llm","description":"The request in plain language, e.g. \\"Show me open, high-priority contracts\\". Structured field filters are not accepted — use Search Records for those."},"fields":{"type":"string","required":true,"visibility":"user-or-llm","description":"Comma-separated field names to return, e.g. \\"id, contract_title1, company_name\\""},"page":{"type":"string","required":false,"visibility":"user-or-llm","description":"Page number, starting from 0"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Records per page"}},"hostedApiKey":"none"},"agiloft_read_record":{"id":"agiloft_read_record","name":"Agiloft Read Record","description":"Read a record by ID from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to read"},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of field names to include in the response"}},"hostedApiKey":"none"},"agiloft_remove_attachment":{"id":"agiloft_remove_attachment","name":"Agiloft Remove Attachment","description":"Remove an attached file from a field in an Agiloft record.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record containing the attachment"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"position":{"type":"string","required":true,"visibility":"user-or-llm","description":"Position index of the file to remove (starting from 0)"}},"hostedApiKey":"none"},"agiloft_retrieve_attachment":{"id":"agiloft_retrieve_attachment","name":"Agiloft Retrieve Attachment","description":"Download an attached file from an Agiloft record field.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record containing the attachment"},"fieldName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the attachment field"},"position":{"type":"string","required":true,"visibility":"user-or-llm","description":"Position index of the file in the field (starting from 0)"}},"hostedApiKey":"none"},"agiloft_run_action_button":{"id":"agiloft_run_action_button","name":"Agiloft Run Action Button","description":"Run an action button on an Agiloft record, such as an approval or send-for-signature step.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"case\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to run the action button on"},"actionButtonField":{"type":"string","required":true,"visibility":"user-or-llm","description":"Logical name of the field holding the action button (e.g., \\"ab_field\\")"}},"hostedApiKey":"none"},"agiloft_saved_search":{"id":"agiloft_saved_search","name":"Agiloft Saved Search","description":"List the saved searches defined for an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Logical table name to list saved searches for (e.g., \\"contract\\")"}},"hostedApiKey":"none"},"agiloft_search_records":{"id":"agiloft_search_records","name":"Agiloft Search Records","description":"Search for records in an Agiloft table using a query.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name to search in (e.g., \\"contracts\\", \\"contacts.employees\\")"},"query":{"type":"string","required":false,"visibility":"user-or-llm","description":"Ad hoc EWSearch query. Combine conditions with && (and) or || (or) and quote every value — e.g. \\"summary~=\'test\'&&priority=\'High\'\\". Required unless a saved search is given."},"search":{"type":"string","required":false,"visibility":"user-or-llm","description":"Label of a saved search defined on the table (e.g., \\"C: Status is Closed\\"). Can be combined with a query to narrow it further."},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of field names to include in the results"},"page":{"type":"string","required":false,"visibility":"user-or-llm","description":"Page number for paginated results (starting from 0)"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of records to return per page. Agiloft treats 0 as \\"all records\\", so leave it unset or use a positive value to keep result sizes bounded."}},"hostedApiKey":"none"},"agiloft_select_records":{"id":"agiloft_select_records","name":"Agiloft Select Records","description":"Select record IDs matching a SQL WHERE clause from an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"where":{"type":"string","required":true,"visibility":"user-or-llm","description":"SQL WHERE clause using database column names (e.g., \\"summary like \'%new%\'\\" or \\"assigned_person=\'John Doe\'\\"). EWSelect has no page size and returns every matching ID, so append a database limit such as \\"limit 0,200\\" to bound the result."}},"hostedApiKey":"none"},"agiloft_update_record":{"id":"agiloft_update_record","name":"Agiloft Update Record","description":"Update an existing record in an Agiloft table.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the record to update"},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Updated field values as a JSON object (e.g., {\\"status\\": \\"Active\\", \\"priority\\": \\"High\\"})"}},"hostedApiKey":"none"},"agiloft_upsert_record":{"id":"agiloft_upsert_record","name":"Agiloft Upsert Record","description":"Create an Agiloft record, or update it when a record already matches the given fields.","version":"1.0.0","params":{"instanceUrl":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft instance URL (e.g., https://mycompany.agiloft.com)"},"knowledgeBase":{"type":"string","required":true,"visibility":"user-only","description":"Knowledge base name"},"login":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft username"},"password":{"type":"string","required":true,"visibility":"user-only","description":"Agiloft password"},"table":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table name (e.g., \\"contracts\\", \\"contacts.employees\\")"},"match":{"type":"string","required":true,"visibility":"user-or-llm","description":"Field used to find an existing record (e.g., \\"ext_id\\"). Pick something that identifies a record uniquely — if more than one record matches, Agiloft writes nothing and returns a conflict."},"async":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Queue the write instead of waiting for it. Returns a callback ID instead of a record ID; pass that to Async Status to poll the result."},"data":{"type":"string","required":true,"visibility":"user-or-llm","description":"Field values as a JSON object. On create these populate the new record; on update only the supplied fields change."}},"hostedApiKey":"none"},"ahrefs_anchors":{"id":"ahrefs_anchors","name":"Ahrefs Anchors","description":"Get the anchor text distribution for a target domain or URL\'s backlinks, showing how many links and referring domains use each anchor text.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live), \\"all_time\\" (default, includes lost backlinks), or \\"since:YYYY-MM-DD\\" (backlinks found since a date)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_backlinks":{"id":"ahrefs_backlinks","name":"Ahrefs Backlinks","description":"Get a list of backlinks pointing to a target domain or URL. Returns details about each backlink including source URL, anchor text, and domain rating.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live backlinks), \\"all_time\\" (default, includes lost backlinks), or \\"since:YYYY-MM-DD\\" (backlinks found since a date)."},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_backlinks_stats":{"id":"ahrefs_backlinks_stats","name":"Ahrefs Backlinks Stats","description":"Get backlink and referring domain totals for a target domain or URL, both currently live and across all time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_batch_analysis":{"id":"ahrefs_batch_analysis","name":"Ahrefs Batch Analysis","description":"Get bulk SEO metrics (Domain Rating, backlinks, referring domains, organic traffic, and more) for multiple domains or URLs in a single request. Useful for comparing many competitors at once.","version":"1.0.0","params":{"targets":{"type":"string","required":true,"visibility":"user-or-llm","description":"Comma-separated list of domains or URLs to analyze. Example: \\"example.com,competitor.com\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode applied to every target: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"protocol":{"type":"string","required":false,"visibility":"user-or-llm","description":"Protocol applied to every target: \\"both\\" (default), \\"http\\", or \\"https\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_broken_backlinks":{"id":"ahrefs_broken_backlinks","name":"Ahrefs Broken Backlinks","description":"Get a list of broken backlinks pointing to a target domain or URL. Useful for identifying link reclamation opportunities.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_domain_rating":{"id":"ahrefs_domain_rating","name":"Ahrefs Domain Rating","description":"Get the Domain Rating (DR) and Ahrefs Rank for a target domain. Domain Rating shows the strength of a website\'s backlink profile on a scale from 0 to 100.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain to analyze (e.g., example.com)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date for historical data in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_domain_rating_history":{"id":"ahrefs_domain_rating_history","name":"Ahrefs Domain Rating History","description":"Get the historical Domain Rating (DR) trend for a target domain or URL over a date range, grouped daily, weekly, or monthly.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_keyword_overview":{"id":"ahrefs_keyword_overview","name":"Ahrefs Keyword Overview","description":"Get detailed metrics for a keyword including search volume, keyword difficulty, CPC, clicks, and traffic potential.","version":"1.0.0","params":{"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The keyword to analyze"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for keyword data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_keywords_history":{"id":"ahrefs_keywords_history","name":"Ahrefs Keywords History","description":"Get the historical organic keyword ranking distribution for a target domain or URL over a date range: how many keywords rank in each position bucket at each point in time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_metrics":{"id":"ahrefs_metrics","name":"Ahrefs Metrics","description":"Get a one-call organic and paid search overview for a target domain or URL: organic traffic, organic keywords, paid traffic, paid keywords, and estimated traffic cost.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_metrics_history":{"id":"ahrefs_metrics_history","name":"Ahrefs Metrics History","description":"Get the historical organic and paid traffic trend for a target domain or URL over a date range: organic traffic/cost and paid traffic/cost at each point in time.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_organic_competitors":{"id":"ahrefs_organic_competitors","name":"Ahrefs Organic Competitors","description":"Get domains that compete with a target domain or URL for the same organic keywords, ranked by keyword overlap.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_organic_keywords":{"id":"ahrefs_organic_keywords","name":"Ahrefs Organic Keywords","description":"Get organic keywords that a target domain or URL ranks for in Google search results. Returns keyword details including search volume, ranking position, and estimated traffic.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for search results. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_paid_pages":{"id":"ahrefs_paid_pages","name":"Ahrefs Paid Pages","description":"Get a target domain\'s pages that receive paid search traffic, sorted by estimated paid traffic. Returns page URLs with their paid traffic, keyword counts, and estimated spend.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_competitors_overview":{"id":"ahrefs_rank_tracker_competitors_overview","name":"Ahrefs Rank Tracker Competitors Overview","description":"Get competitor rankings for the keywords tracked in an Ahrefs Rank Tracker project: each tracked keyword\'s volume and difficulty alongside every competitor\'s position, traffic, and traffic value. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report rankings for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"dateCompared":{"type":"string","required":false,"visibility":"user-only","description":"Comparison date in YYYY-MM-DD format, to compute position/traffic deltas"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_competitors_stats":{"id":"ahrefs_rank_tracker_competitors_stats","name":"Ahrefs Rank Tracker Competitors Stats","description":"Get aggregate competitor stats for an Ahrefs Rank Tracker project: each competitor\'s traffic, traffic value, average position, and share of voice across all tracked keywords. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report metrics for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_overview":{"id":"ahrefs_rank_tracker_overview","name":"Ahrefs Rank Tracker Overview","description":"Get ranking overview metrics for the keywords tracked in an Ahrefs Rank Tracker project: position, search volume, keyword difficulty, and estimated traffic. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":true,"visibility":"user-only","description":"Date to report rankings for, in YYYY-MM-DD format"},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"dateCompared":{"type":"string","required":false,"visibility":"user-only","description":"Comparison date in YYYY-MM-DD format, to compute position/traffic deltas"},"volumeMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search volume calculation: \\"monthly\\" or \\"average\\" (default: \\"monthly\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_rank_tracker_serp_overview":{"id":"ahrefs_rank_tracker_serp_overview","name":"Ahrefs Rank Tracker SERP Overview","description":"Get the full SERP (search engine results page) for a keyword tracked in an Ahrefs Rank Tracker project, including every ranking URL with its position, title, and authority metrics. This endpoint is free and does not consume API units.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Rank Tracker project ID (found in the project URL in Ahrefs)"},"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The tracked keyword to retrieve SERP data for"},"country":{"type":"string","required":true,"visibility":"user-or-llm","description":"Country code for the tracked keyword. Example: \\"us\\", \\"gb\\", \\"de\\""},"device":{"type":"string","required":true,"visibility":"user-or-llm","description":"Rankings device type: \\"desktop\\" or \\"mobile\\""},"topPositions":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of top organic positions to return (defaults to all available)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Timestamp to return the last available SERP Overview at, in YYYY-MM-DDThh:mm:ss format"},"locationId":{"type":"number","required":false,"visibility":"user-or-llm","description":"Location ID of the tracked keyword, if tracked at a specific location"},"languageCode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Language code of the tracked keyword"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_refdomains_history":{"id":"ahrefs_refdomains_history","name":"Ahrefs Referring Domains History","description":"Get the historical referring domains trend for a target domain or URL over a date range, grouped daily, weekly, or monthly.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\""},"dateFrom":{"type":"string","required":true,"visibility":"user-only","description":"Start date of the historical period, in YYYY-MM-DD format"},"dateTo":{"type":"string","required":false,"visibility":"user-only","description":"End date of the historical period, in YYYY-MM-DD format (defaults to today)"},"historyGrouping":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval for grouping data points: \\"daily\\", \\"weekly\\", or \\"monthly\\" (default: \\"monthly\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_referring_domains":{"id":"ahrefs_referring_domains","name":"Ahrefs Referring Domains","description":"Get a list of domains that link to a target domain or URL. Returns unique referring domains with their domain rating, backlink counts, and discovery dates.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain or URL to analyze. Example: \\"example.com\\" or \\"https://example.com/page\\""},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"history":{"type":"string","required":false,"visibility":"user-or-llm","description":"Historical scope: \\"live\\" (currently live), \\"all_time\\" (default, includes lost domains), or \\"since:YYYY-MM-DD\\" (domains found since a date)."},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_related_terms":{"id":"ahrefs_related_terms","name":"Ahrefs Related Terms","description":"Get keyword ideas related to a seed keyword: terms the same top-ranking pages also rank for (\\"also rank for\\") or also discuss (\\"also talk about\\"), with volume, difficulty, and CPC.","version":"1.0.0","params":{"keyword":{"type":"string","required":true,"visibility":"user-or-llm","description":"The seed keyword to find related terms for"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for keyword data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"terms":{"type":"string","required":false,"visibility":"user-or-llm","description":"Type of related keywords to return: \\"also_rank_for\\", \\"also_talk_about\\", or \\"all\\" (default: \\"all\\")"},"viewFor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Whether to derive related terms from the top 10 or top 100 ranking pages (default: \\"top_10\\")"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_site_audit_page_explorer":{"id":"ahrefs_site_audit_page_explorer","name":"Ahrefs Site Audit Page Explorer","description":"Get crawled pages from an Ahrefs Site Audit project with health and SEO metrics: HTTP status, title, link counts, backlinks, indexability, and traffic. Optionally filter to pages affected by a specific issue.","version":"1.0.0","params":{"projectId":{"type":"number","required":true,"visibility":"user-or-llm","description":"The Site Audit project ID (found in the project URL in Ahrefs)"},"date":{"type":"string","required":false,"visibility":"user-only","description":"Crawl date in YYYY-MM-DDThh:mm:ss format (defaults to the most recent crawl)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of results to skip, for pagination"},"issueId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Only return pages affected by this issue ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ahrefs_top_pages":{"id":"ahrefs_top_pages","name":"Ahrefs Top Pages","description":"Get the top pages of a target domain sorted by organic traffic. Returns page URLs with their traffic, keyword counts, and estimated traffic value.","version":"1.0.0","params":{"target":{"type":"string","required":true,"visibility":"user-or-llm","description":"The target domain to analyze. Example: \\"example.com\\""},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Country code for traffic data. Example: \\"us\\", \\"gb\\", \\"de\\" (default: \\"us\\")"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains, default), exact (exact URL match). Example: \\"domain\\""},"date":{"type":"string","required":false,"visibility":"user-only","description":"Date to report metrics on, in YYYY-MM-DD format (defaults to today)"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of results to return. Example: 50 (default: 1000)"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ahrefs API Key"}},"oauth":{"required":false,"provider":"ahrefs","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"airtable_create_records":{"id":"airtable_create_records","name":"Airtable Create Records","description":"Write new records to an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to create, each with a `fields` object"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_delete_records":{"id":"airtable_delete_records","name":"Airtable Delete Records","description":"Delete one or more records from an Airtable table by ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordIds":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of record IDs to delete (each starts with \\"rec\\", e.g., [\\"recXXXXXXXXXXXXXX\\"]). Pass a single-element array to delete one record."}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_get_base_schema":{"id":"airtable_get_base_schema","name":"Airtable Get Base Schema","description":"Get the schema of all tables, fields, and views in an Airtable base","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_get_record":{"id":"airtable_get_record","name":"Airtable Get Record","description":"Retrieve a single record from an Airtable table by its ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record ID to retrieve (starts with \\"rec\\", e.g., \\"recXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_bases":{"id":"airtable_list_bases","name":"Airtable List Bases","description":"List all bases the authenticated user has access to","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"offset":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination offset for retrieving additional bases"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_records":{"id":"airtable_list_records","name":"Airtable List Records","description":"Read records from an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"maxRecords":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of records to return (default: all records)"},"filterFormula":{"type":"string","required":false,"visibility":"user-or-llm","description":"Formula to filter records (e.g., \\"({Field Name} = \'Value\')\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_list_tables":{"id":"airtable_list_tables","name":"Airtable List Tables","description":"List all tables and their schema in an Airtable base","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_update_multiple_records":{"id":"airtable_update_multiple_records","name":"Airtable Update Multiple Records","description":"Update multiple existing records in an Airtable table","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to update, each with an `id` and a `fields` object"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_update_record":{"id":"airtable_update_record","name":"Airtable Update Record","description":"Update an existing record in an Airtable table by ID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"recordId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Record ID to update (starts with \\"rec\\", e.g., \\"recXXXXXXXXXXXXXX\\")"},"fields":{"type":"json","required":true,"visibility":"user-or-llm","description":"An object containing the field names and their new values"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"airtable_upsert_records":{"id":"airtable_upsert_records","name":"Airtable Upsert Records","description":"Update existing records or create new ones in an Airtable table, matching on the specified merge fields","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token"},"baseId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Airtable base ID (starts with \\"app\\", e.g., \\"appXXXXXXXXXXXXXX\\")"},"tableId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Table ID (starts with \\"tbl\\") or table name"},"records":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of records to upsert, each with a `fields` object"},"fieldsToMergeOn":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of field names used to match existing records (max 3). A record is updated when all merge fields match, otherwise it is created. Example: [\\"Name\\"]"},"typecast":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, Airtable automatically converts string values to the field type"}},"oauth":{"required":true,"provider":"airtable"},"hostedApiKey":"none"},"algolia_add_record":{"id":"algolia_add_record","name":"Algolia Add Record","description":"Add or replace a record in an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":false,"visibility":"user-or-llm","description":"Object ID for the record (auto-generated if not provided)"},"record":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object representing the record to add"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_batch_operations":{"id":"algolia_batch_operations","name":"Algolia Batch Operations","description":"Perform batch add, update, partial update, or delete operations on records in an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"requests":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of batch operations. Each item has \\"action\\" (addObject, updateObject, partialUpdateObject, partialUpdateObjectNoCreate, deleteObject, delete, clear) and \\"body\\" (the record data; must include objectID for update/delete; use an empty object {} for the index-level delete/clear actions)"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_browse_records":{"id":"algolia_browse_records","name":"Algolia Browse Records","description":"Browse and iterate over all records in an Algolia index using cursor pagination","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key (must have browse ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to browse"},"query":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search query to filter browsed records"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter string to narrow down results"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of hits per page (default: 1000, max: 1000)"},"cursor":{"type":"string","required":false,"visibility":"user-or-llm","description":"Cursor from a previous browse response for pagination"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_clear_records":{"id":"algolia_clear_records","name":"Algolia Clear Records","description":"Clear all records from an Algolia index while keeping settings, synonyms, and rules","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to clear"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_copy_move_index":{"id":"algolia_copy_move_index","name":"Algolia Copy/Move Index","description":"Copy or move an Algolia index to a new destination","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the source index"},"operation":{"type":"string","required":true,"visibility":"user-or-llm","description":"Operation to perform: \\"copy\\" or \\"move\\""},"destination":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the destination index"},"scope":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of scopes to copy (only for \\"copy\\" operation): [\\"settings\\", \\"synonyms\\", \\"rules\\"]. Omit to copy everything including records."}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_delete_by_filter":{"id":"algolia_delete_by_filter","name":"Algolia Delete By Filter","description":"Delete all records matching a filter from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter expression to match records for deletion (e.g., \\"category:outdated\\")"},"facetFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of facet filters (e.g., [\\"brand:Acme\\"])"},"numericFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of numeric filters (e.g., [\\"price > 100\\"])"},"tagFilters":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of tag filters using the _tags attribute (e.g., [\\"published\\"])"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search filter (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search filter"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search filter"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_delete_index":{"id":"algolia_delete_index","name":"Algolia Delete Index","description":"Delete an entire Algolia index and all its records","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have deleteIndex ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to delete"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_delete_record":{"id":"algolia_delete_record","name":"Algolia Delete Record","description":"Delete a record by objectID from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to delete"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_get_record":{"id":"algolia_get_record","name":"Algolia Get Record","description":"Get a record by objectID from an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to retrieve"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_get_records":{"id":"algolia_get_records","name":"Algolia Get Records","description":"Retrieve multiple records by objectID from one or more Algolia indices","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Default index name for all requests"},"requests":{"type":"json","required":true,"visibility":"user-or-llm","description":"Array of objects specifying records to retrieve. Each must have \\"objectID\\" and optionally \\"indexName\\" and \\"attributesToRetrieve\\"."}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_get_settings":{"id":"algolia_get_settings","name":"Algolia Get Settings","description":"Retrieve the settings of an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_get_task_status":{"id":"algolia_get_task_status","name":"Algolia Get Task Status","description":"Check whether an Algolia indexing task has finished publishing","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index the task ran against"},"taskID":{"type":"number","required":true,"visibility":"user-or-llm","description":"The taskID returned by a previous write operation"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_list_indices":{"id":"algolia_list_indices","name":"Algolia List Indices","description":"List all indices in an Algolia application","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for paginating indices (default: not paginated)"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of indices per page (default: 100)"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_partial_update_record":{"id":"algolia_partial_update_record","name":"Algolia Partial Update Record","description":"Partially update a record in an Algolia index without replacing it entirely","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"objectID":{"type":"string","required":true,"visibility":"user-or-llm","description":"The objectID of the record to update"},"attributes":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object with attributes to update. Supports built-in operations like {\\"stock\\": {\\"_operation\\": \\"Decrement\\", \\"value\\": 1}}"},"createIfNotExists":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to create the record if it does not exist (default: true)"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_search":{"id":"algolia_search","name":"Algolia Search","description":"Search an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia API Key"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index to search"},"query":{"type":"string","required":true,"visibility":"user-or-llm","description":"Search query text"},"hitsPerPage":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of hits per page (default: 20)"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number to retrieve (default: 0)"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter string (e.g., \\"category:electronics AND price < 100\\")"},"attributesToRetrieve":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of attributes to retrieve"},"facets":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of facet attribute names to retrieve counts for (use \\"*\\" for all)"},"getRankingInfo":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to include detailed ranking information in each hit"},"aroundLatLng":{"type":"string","required":false,"visibility":"user-or-llm","description":"Coordinates for geo-search (e.g., \\"40.71,-74.01\\")"},"aroundRadius":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum radius in meters for geo-search, or \\"all\\" for unlimited"},"insideBoundingBox":{"type":"json","required":false,"visibility":"user-or-llm","description":"Bounding box coordinates as [[lat1, lng1, lat2, lng2]] for geo-search"},"insidePolygon":{"type":"json","required":false,"visibility":"user-or-llm","description":"Polygon coordinates as [[lat1, lng1, lat2, lng2, lat3, lng3, ...]] for geo-search"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"algolia_update_settings":{"id":"algolia_update_settings","name":"Algolia Update Settings","description":"Update the settings of an Algolia index","version":"1.0","params":{"applicationId":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Application ID"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Algolia Admin API Key (must have editSettings ACL)"},"indexName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the Algolia index"},"settings":{"type":"json","required":true,"visibility":"user-or-llm","description":"JSON object with settings to update (e.g., {\\"searchableAttributes\\": [\\"name\\", \\"description\\"], \\"customRanking\\": [\\"desc(popularity)\\"]})"},"forwardToReplicas":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to apply changes to replica indices (default: false)"}},"oauth":{"required":false,"provider":"algolia","credentialKind":"service-account","credentialParams":{"applicationId":"providerWorkspaceId","apiKey":"accessToken"},"credentialSuppliedParams":["applicationId","apiKey"]},"hostedApiKey":"none"},"amplitude_event_segmentation":{"id":"amplitude_event_segmentation","name":"Amplitude Event Segmentation","description":"Query event analytics data with segmentation. Get event counts, uniques, averages, and more.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"eventType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Event type name to analyze"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric type: uniques, totals, pct_dau, average, histogram, sums, value_avg, or formula (default: uniques)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by (prefix custom user properties with \\"gp:\\")"},"groupBy2":{"type":"string","required":false,"visibility":"user-or-llm","description":"Second property name to group by (prefix custom user properties with \\"gp:\\")"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of group-by values (max 1000)"},"filters":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON array of filter objects applied to the event, e.g. [{\\"subprop_type\\":\\"event\\",\\"subprop_key\\":\\"city\\",\\"subprop_op\\":\\"is\\",\\"subprop_value\\":[\\"San Francisco\\"]}]"},"formula":{"type":"string","required":false,"visibility":"user-or-llm","description":"Required when metric is \\"formula\\", e.g. \\"UNIQUES(A)/UNIQUES(B)\\""},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_funnels":{"id":"amplitude_funnels","name":"Amplitude Funnels","description":"Analyze conversion rates and drop-off between a sequence of events.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"events":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON array of event objects, one per funnel step in order, e.g. [{\\"event_type\\":\\"signup\\"},{\\"event_type\\":\\"purchase\\"}]"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"mode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Funnel ordering: \\"ordered\\", \\"unordered\\", or \\"sequential\\" (default: ordered)"},"userType":{"type":"string","required":false,"visibility":"user-or-llm","description":"User type: \\"new\\" or \\"active\\" (default: active)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: -300000 (real-time), -3600000 (hourly), 1 (daily), 7 (weekly), or 30 (monthly)"},"conversionWindowSeconds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Conversion window in seconds (default: 2592000, i.e. 30 days)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property to group by (limit: one; prefix custom properties with \\"gp:\\")"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of group-by values (default: 100, max: 1000)"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_get_active_users":{"id":"amplitude_get_active_users","name":"Amplitude Get Active Users","description":"Get active or new user counts over a date range from the Dashboard REST API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric type: \\"active\\" or \\"new\\" (default: active)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_get_revenue":{"id":"amplitude_get_revenue","name":"Amplitude Get Revenue","description":"Get revenue LTV data including ARPU, ARPPU, total revenue, and paying user counts.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"metric":{"type":"string","required":false,"visibility":"user-or-llm","description":"Metric: 0 (ARPU), 1 (ARPPU), 2 (Total Revenue), 3 (Paying Users)"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property name to group by (limit: one)"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_group_identify":{"id":"amplitude_group_identify","name":"Amplitude Group Identify","description":"Set group-level properties in Amplitude. Supports $set, $setOnce, $add, $append, $unset operations.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"groupType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Group classification (e.g., \\"company\\", \\"org_id\\")"},"groupValue":{"type":"string","required":true,"visibility":"user-or-llm","description":"Specific group identifier (e.g., \\"Acme Corp\\")"},"groupProperties":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON object of group properties. Use operations like $set, $setOnce, $add, $append, $unset."},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_identify_user":{"id":"amplitude_identify_user","name":"Amplitude Identify User","description":"Set user properties in Amplitude using the Identify API. Supports $set, $setOnce, $add, $append, $unset operations.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"User ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"userProperties":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON object of user properties. Use operations like $set, $setOnce, $add, $append, $unset."},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_list_events":{"id":"amplitude_list_events","name":"Amplitude List Events","description":"List all event types in the Amplitude project with their weekly totals and unique counts.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_realtime_active_users":{"id":"amplitude_realtime_active_users","name":"Amplitude Real-time Active Users","description":"Get real-time active user counts at 5-minute granularity for the last 2 days.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_retention":{"id":"amplitude_retention","name":"Amplitude Retention","description":"Measure how many users return to perform an action after a starting action.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"startEvent":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON starting event object, e.g. {\\"event_type\\":\\"_new\\"} or {\\"event_type\\":\\"_active\\"}"},"returnEvent":{"type":"string","required":true,"visibility":"user-or-llm","description":"JSON returning event object, e.g. {\\"event_type\\":\\"_all\\"} or {\\"event_type\\":\\"_active\\"}"},"start":{"type":"string","required":true,"visibility":"user-or-llm","description":"Start date in YYYYMMDD format"},"end":{"type":"string","required":true,"visibility":"user-or-llm","description":"End date in YYYYMMDD format"},"retentionMode":{"type":"string","required":false,"visibility":"user-or-llm","description":"Retention type: \\"bracket\\", \\"rolling\\", or \\"n-day\\" (default: n-day)"},"retentionBrackets":{"type":"string","required":false,"visibility":"user-or-llm","description":"Required when Retention Mode is \\"bracket\\". Day ranges, e.g. [[0,4]]"},"interval":{"type":"string","required":false,"visibility":"user-or-llm","description":"Time interval: 1 (daily), 7 (weekly), or 30 (monthly)"},"groupBy":{"type":"string","required":false,"visibility":"user-or-llm","description":"Property to group by (limit: one; prefix custom properties with \\"gp:\\")"},"segment":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON segment definition(s) applied to the query"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_send_event":{"id":"amplitude_send_event","name":"Amplitude Send Event","description":"Track an event in Amplitude using the HTTP V2 API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"User ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"eventType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the event (e.g., \\"page_view\\", \\"purchase\\")"},"eventProperties":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON object of custom event properties"},"userProperties":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON object of user properties to set (supports $set, $setOnce, $add, $append, $unset)"},"time":{"type":"string","required":false,"visibility":"user-or-llm","description":"Event timestamp in milliseconds since epoch"},"sessionId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Session start time in milliseconds since epoch"},"insertId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Unique ID for deduplication (within 7-day window)"},"appVersion":{"type":"string","required":false,"visibility":"user-or-llm","description":"Application version string"},"platform":{"type":"string","required":false,"visibility":"user-or-llm","description":"Platform (e.g., \\"Web\\", \\"iOS\\", \\"Android\\")"},"country":{"type":"string","required":false,"visibility":"user-or-llm","description":"Two-letter country code"},"language":{"type":"string","required":false,"visibility":"user-or-llm","description":"Language code (e.g., \\"en\\")"},"ip":{"type":"string","required":false,"visibility":"user-or-llm","description":"IP address for geo-location"},"price":{"type":"string","required":false,"visibility":"user-or-llm","description":"Price of the item purchased"},"quantity":{"type":"string","required":false,"visibility":"user-or-llm","description":"Quantity of items purchased"},"revenue":{"type":"string","required":false,"visibility":"user-or-llm","description":"Revenue amount"},"productId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Product identifier"},"revenueType":{"type":"string","required":false,"visibility":"user-or-llm","description":"Revenue type (e.g., \\"purchase\\", \\"refund\\")"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_user_activity":{"id":"amplitude_user_activity","name":"Amplitude User Activity","description":"Get the event stream for a specific user by their Amplitude ID.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"amplitudeId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Amplitude internal user ID"},"offset":{"type":"string","required":false,"visibility":"user-or-llm","description":"Offset for pagination (default 0)"},"limit":{"type":"string","required":false,"visibility":"user-or-llm","description":"Maximum number of events to return (default 1000, max 1000)"},"direction":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort direction: \\"latest\\" or \\"earliest\\" (default: latest)"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"amplitude_user_profile":{"id":"amplitude_user_profile","name":"Amplitude User Profile","description":"Get a user profile including properties, cohort memberships, and computed properties. Not available for EU data-residency projects.","version":"1.0.0","params":{"apiKey":{"type":"string","required":false,"visibility":"user-only","description":"Amplitude API Key. With it, the request authenticates with both keys over Basic auth"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"userId":{"type":"string","required":false,"visibility":"user-or-llm","description":"External user ID (required if no device_id)"},"deviceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Device ID (required if no user_id)"},"getAmpProps":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include Amplitude user properties (true/false, default: false)"},"getCohortIds":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include cohort IDs the user belongs to (true/false, default: false)"},"getComputations":{"type":"string","required":false,"visibility":"user-or-llm","description":"Include computed user properties (true/false, default: false)"}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken"},"credentialSuppliedParams":["apiKey","secretKey"]},"hostedApiKey":"none"},"amplitude_user_search":{"id":"amplitude_user_search","name":"Amplitude User Search","description":"Search for a user by User ID, Device ID, or Amplitude ID using the Dashboard REST API.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude API Key"},"secretKey":{"type":"string","required":true,"visibility":"user-only","description":"Amplitude Secret Key"},"user":{"type":"string","required":true,"visibility":"user-or-llm","description":"User ID, Device ID, or Amplitude ID to search for"},"dataResidency":{"type":"string","required":false,"visibility":"user-or-llm","description":"Data residency region: \\"us\\" (default) or \\"eu\\""}},"oauth":{"required":false,"provider":"amplitude","credentialKind":"service-account","credentialParams":{"apiKey":"providerWorkspaceId","secretKey":"accessToken","dataResidency":"domain"},"credentialSuppliedParams":["apiKey","secretKey","dataResidency"]},"hostedApiKey":"none"},"apify_get_dataset_items":{"id":"apify_get_dataset_items","name":"APIFY Get Dataset Items","description":"Retrieve items stored in an APIFY dataset","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"datasetId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Dataset ID to read items from. Example: \\"9RnD3Pql2vGZkc5H5\\""},"itemLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Max items to return (1-250000). Default: all items. Example: 500"},"offset":{"type":"number","required":false,"visibility":"user-or-llm","description":"Number of items to skip at the start. Default: 0"},"fields":{"type":"string","required":false,"visibility":"user-or-llm","description":"Comma-separated list of fields to include. Example: \\"title,url,price\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_get_run":{"id":"apify_get_run","name":"APIFY Get Run","description":"Get the status and details of an APIFY actor run","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"runId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor run ID to fetch. Example: \\"HG7ML7M8z78YcAPEB\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_actor_async":{"id":"apify_run_actor_async","name":"APIFY Run Actor (Async)","description":"Start an APIFY Actor run and poll it within the same workflow step until completion or the polling deadline","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"actorId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor ID or username/actor-name. Examples: \\"apify/web-scraper\\", \\"janedoe/my-actor\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor input as JSON string. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ], \\"maxPages\\": 10 }"},"waitForFinish":{"type":"number","required":false,"visibility":"user-or-llm","description":"Initial wait time in seconds (0-60) before polling starts. Example: 30"},"itemLimit":{"type":"number","required":false,"default":100,"visibility":"user-or-llm","description":"Max dataset items to fetch (1-250000). Default: 100. Example: 500"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the actor run (128-32768). Example: 1024 for 1GB, 2048 for 2GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the actor run. Example: 300 for 5 minutes, 3600 for 1 hour"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\", \\"build-tag-name\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_actor_sync":{"id":"apify_run_actor_sync","name":"APIFY Run Actor (Sync)","description":"Run an APIFY actor synchronously and get results (max 5 minutes)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"actorId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Actor ID or username/actor-name. Examples: \\"apify/web-scraper\\", \\"janedoe/my-actor\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor input as JSON string. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ], \\"maxPages\\": 10 }"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the actor run (128-32768). Example: 1024 for 1GB, 2048 for 2GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the actor run. Example: 300 for 5 minutes, 3600 for 1 hour"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\", \\"build-tag-name\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apify_run_task":{"id":"apify_run_task","name":"APIFY Run Task","description":"Run a saved APIFY actor task synchronously and get dataset items (max 5 minutes)","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"APIFY API token from console.apify.com/account#/integrations"},"taskId":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task ID or username/task-name. Examples: \\"janedoe/my-task\\", \\"moJRLRc85AitArpNN\\""},"input":{"type":"string","required":false,"visibility":"user-or-llm","description":"JSON string that overrides the task\'s saved input. Example: { \\"startUrls\\": [ { \\"url\\": \\"https://example.com\\" } ] }"},"itemLimit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Max dataset items to return (1-250000). Example: 500"},"memory":{"type":"number","required":false,"visibility":"user-or-llm","description":"Memory in megabytes allocated for the run (128-32768). Example: 1024 for 1GB"},"timeout":{"type":"number","required":false,"visibility":"user-or-llm","description":"Timeout in seconds for the run. Example: 300 for 5 minutes"},"build":{"type":"string","required":false,"visibility":"user-or-llm","description":"Actor build to run. Examples: \\"latest\\", \\"beta\\", \\"1.2.3\\""}},"oauth":{"required":false,"provider":"apify","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"apollo_account_bulk_create":{"id":"apollo_account_bulk_create","name":"Apollo Bulk Create Accounts","description":"Create up to 100 accounts at once in your Apollo database. Set run_dedupe=true to deduplicate by domain, organization_id, and name. Master key required.","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"accounts":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of accounts to create (max 100). Each account should include a name, and may optionally include domain, phone, phone_status_cd, raw_address, owner_id, linkedin_url, facebook_url, twitter_url, salesforce_id, and hubspot_id."},"append_label_names":{"type":"array","required":false,"visibility":"user-only","description":"Array of label names to add to ALL accounts in this request"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"When true, performs aggressive deduplication by domain, organization_id, and name (defaults to false)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_account_bulk_update":{"id":"apollo_account_bulk_update","name":"Apollo Bulk Update Accounts","description":"Update up to 1000 existing accounts at once in your Apollo database (higher limit than contacts!). Each account must include an id field. Master key required.","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"account_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of account IDs to update with the same values (max 1000). Use with name/owner_id for uniform updates. Use either this OR account_attributes."},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this name to all accounts"},"owner_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this owner to all accounts"},"account_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"When using account_ids, apply this account stage to all accounts"},"account_attributes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of account objects with individual updates (each must include id). Example: [{\\"id\\": \\"acc1\\", \\"name\\": \\"Acme\\", \\"owner_id\\": \\"u1\\", \\"account_stage_id\\": \\"s1\\", \\"typed_custom_fields\\": {\\"field_id\\": \\"value\\"}}]"},"async":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, processes the update asynchronously. Only supported when using account_ids; returns 422 if used with account_attributes."}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_account_create":{"id":"apollo_account_create","name":"Apollo Create Account","description":"Create a new account (company) in your Apollo database","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Company name (e.g., \\"Acme Corporation\\")"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain without www. prefix (e.g., \\"acme.com\\")"},"phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number for the account"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo user ID of the account owner"},"account_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo ID for the account stage to assign this account to"},"raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate location (e.g., \\"San Francisco, CA, USA\\")"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_account_search":{"id":"apollo_account_search","name":"Apollo Search Accounts","description":"Search your team\'s accounts in Apollo. Display limit: 50,000 records (100 records per page, 500 pages max). Use filters to narrow results. Master key required.","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"q_organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter accounts by organization name (partial-match search)"},"account_stage_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by account stage IDs"},"account_label_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by account label IDs"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"account_last_activity_date\\", \\"account_created_at\\", or \\"account_updated_at\\""},"sort_ascending":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Sort ascending when true. Defaults to descending."},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_account_update":{"id":"apollo_account_update","name":"Apollo Update Account","description":"Update an existing account in your Apollo database","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"account_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the account to update (e.g., \\"acc_abc123\\")"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company name (e.g., \\"Acme Corporation\\")"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain (e.g., \\"acme.com\\")"},"phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company phone number"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo user ID of the account owner"},"account_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"Apollo ID for the account stage to assign this account to"},"raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate location (e.g., \\"San Francisco, CA, USA\\")"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_contact_bulk_create":{"id":"apollo_contact_bulk_create","name":"Apollo Bulk Create Contacts","description":"Create up to 100 contacts at once in your Apollo database. Supports deduplication to prevent creating duplicate contacts. Master key required.","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"contacts":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of contacts to create (max 100). Each contact may include first_name, last_name, email, title, organization_name, account_id, owner_id, contact_stage_id, linkedin_url, phone (single string) or phone_numbers (array of {raw_number, position}), contact_emails, typed_custom_fields, and CRM IDs (salesforce_contact_id, hubspot_id, team_id) for cross-system matching"},"append_label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Label names to add to all contacts in this request (e.g., [\\"Hot Lead\\"])"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-only","description":"Enable deduplication to prevent creating duplicate contacts. When true, existing contacts are returned without modification"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_contact_bulk_update":{"id":"apollo_contact_bulk_update","name":"Apollo Bulk Update Contacts","description":"Update up to 100 existing contacts at once in your Apollo database. Each contact must include an id field. Master key required.","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"contact_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of contact IDs to update. Must be paired with an object-form contact_attributes specifying the fields to apply uniformly to all listed contacts."},"contact_attributes":{"type":"json","required":false,"visibility":"user-or-llm","description":"Required. Either an array of per-contact updates (each with id) — used standalone — or a single object of attributes to apply to all contact_ids. Supported fields: owner_id, email, organization_name, title, first_name, last_name, account_id, present_raw_address, linkedin_url, typed_custom_fields"},"async":{"type":"boolean","required":false,"visibility":"user-only","description":"Force asynchronous processing. Automatically enabled for >100 contacts"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_contact_create":{"id":"apollo_contact_create","name":"Apollo Create Contact","description":"Create a new contact in your Apollo database","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"first_name":{"type":"string","required":true,"visibility":"user-or-llm","description":"First name of the contact"},"last_name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Last name of the contact"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address of the contact"},"title":{"type":"string","required":false,"visibility":"user-or-llm","description":"Job title (e.g., \\"VP of Sales\\", \\"Software Engineer\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo account ID to associate with (e.g., \\"acc_abc123\\")"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the contact owner (accepted by Apollo but not officially documented for POST /contacts)"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the contact\'s employer (e.g., \\"Apollo\\")"},"website_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate website URL (e.g., \\"https://www.apollo.io/\\")"},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Lists/labels to add the contact to (e.g., [\\"Prospects\\"])"},"contact_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the contact stage"},"present_raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal location for the contact (e.g., \\"Atlanta, United States\\")"},"direct_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number"},"corporate_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Work/office phone number"},"mobile_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Mobile phone number"},"home_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Home phone number"},"other_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Alternative phone number"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom field values keyed by custom field ID"},"run_dedupe":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, Apollo deduplicates against existing contacts"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_contact_search":{"id":"apollo_contact_search","name":"Apollo Search Contacts","description":"Search your team\'s contacts in Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"q_keywords":{"type":"string","required":false,"visibility":"user-or-llm","description":"Keywords to search for"},"contact_stage_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by contact stage IDs"},"contact_label_ids":{"type":"array","required":false,"visibility":"user-only","description":"Filter by Apollo label IDs (lists)"},"sort_by_field":{"type":"string","required":false,"visibility":"user-only","description":"Sort field: contact_last_activity_date, contact_email_last_opened_at, contact_email_last_clicked_at, contact_created_at, or contact_updated_at"},"sort_ascending":{"type":"boolean","required":false,"visibility":"user-only","description":"When true, sort ascending. Must be used together with sort_by_field"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_contact_update":{"id":"apollo_contact_update","name":"Apollo Update Contact","description":"Update an existing contact in your Apollo database","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"contact_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the contact to update (e.g., \\"con_abc123\\")"},"first_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"First name of the contact"},"last_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Last name of the contact"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address"},"title":{"type":"string","required":false,"visibility":"user-or-llm","description":"Job title (e.g., \\"VP of Sales\\", \\"Software Engineer\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo account ID (e.g., \\"acc_abc123\\")"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the contact owner (accepted by Apollo but not officially documented for PATCH /contacts/{id})"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the contact\'s employer (e.g., \\"Apollo\\")"},"website_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"Corporate website URL (e.g., \\"https://www.apollo.io/\\")"},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Lists/labels to add the contact to (e.g., [\\"Prospects\\"])"},"contact_stage_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the contact stage"},"present_raw_address":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal location for the contact (e.g., \\"Atlanta, United States\\")"},"direct_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number"},"corporate_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Work/office phone number"},"mobile_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Mobile phone number"},"home_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Home phone number"},"other_phone":{"type":"string","required":false,"visibility":"user-or-llm","description":"Alternative phone number"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-or-llm","description":"Custom field values keyed by custom field ID"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_email_accounts":{"id":"apollo_email_accounts","name":"Apollo Get Email Accounts","description":"Get list of team\'s linked email accounts in Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_opportunity_create":{"id":"apollo_opportunity_create","name":"Apollo Create Opportunity","description":"Create a new deal for an account in your Apollo database (master key required)","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the opportunity/deal (e.g., \\"Enterprise License - Q1\\")"},"account_id":{"type":"string","required":false,"visibility":"user-or-llm","description":"ID of the account this opportunity belongs to (e.g., \\"acc_abc123\\")"},"amount":{"type":"string","required":false,"visibility":"user-or-llm","description":"Monetary value as a plain number string with no commas or currency symbols"},"opportunity_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the opportunity stage"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the opportunity owner"},"closed_date":{"type":"string","required":false,"visibility":"user-or-llm","description":"Expected close date in YYYY-MM-DD format"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_opportunity_get":{"id":"apollo_opportunity_get","name":"Apollo Get Opportunity","description":"Retrieve complete details of a specific deal/opportunity by ID","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"opportunity_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the opportunity to retrieve (e.g., \\"opp_abc123\\")"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_opportunity_search":{"id":"apollo_opportunity_search","name":"Apollo Search Opportunities","description":"Search and list all deals/opportunities in your team\'s Apollo account","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"amount\\", \\"is_closed\\", or \\"is_won\\""},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_opportunity_update":{"id":"apollo_opportunity_update","name":"Apollo Update Opportunity","description":"Update an existing deal/opportunity in your Apollo database","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"opportunity_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the opportunity to update (e.g., \\"opp_abc123\\")"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Name of the opportunity/deal (e.g., \\"Enterprise License - Q1\\")"},"amount":{"type":"string","required":false,"visibility":"user-or-llm","description":"Monetary value as a plain number string with no commas or currency symbols"},"opportunity_stage_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the opportunity stage"},"owner_id":{"type":"string","required":false,"visibility":"user-only","description":"User ID of the opportunity owner"},"closed_date":{"type":"string","required":false,"visibility":"user-or-llm","description":"Expected close date in YYYY-MM-DD format"},"typed_custom_fields":{"type":"json","required":false,"visibility":"user-only","description":"Custom field values as { custom_field_id: value } map"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_organization_bulk_enrich":{"id":"apollo_organization_bulk_enrich","name":"Apollo Bulk Organization Enrichment","description":"Enrich data for up to 10 organizations at once using Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"domains":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of company domains to enrich (max 10, no www. or @, e.g., [\\"apollo.io\\", \\"stripe.com\\"])"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_organization_enrich":{"id":"apollo_organization_enrich","name":"Apollo Organization Enrichment","description":"Enrich data for a single organization using Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"domain":{"type":"string","required":true,"visibility":"user-or-llm","description":"Company domain (e.g., \\"apollo.io\\", \\"acme.com\\")"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_organization_search":{"id":"apollo_organization_search","name":"Apollo Organization Search","description":"Search Apollo\'s database for companies using filters","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"organization_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Company HQ locations (cities, US states, or countries)"},"organization_not_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Exclude companies whose HQ is in these locations"},"organization_num_employees_ranges":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employee count ranges as \\"min,max\\" strings (e.g., [\\"1,10\\", \\"250,500\\", \\"10000,20000\\"])"},"q_organization_keyword_tags":{"type":"array","required":false,"visibility":"user-or-llm","description":"Industry or keyword tags"},"q_organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Organization name to search for (e.g., \\"Acme\\", \\"TechCorp\\")"},"organization_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Apollo organization IDs to include (e.g., [\\"5e66b6381e05b4008c8331b8\\"])"},"q_organization_domains_list":{"type":"array","required":false,"visibility":"user-or-llm","description":"Domain names to filter by (no www. or @, up to 1,000)"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_people_bulk_enrich":{"id":"apollo_people_bulk_enrich","name":"Apollo Bulk People Enrichment","description":"Enrich data for up to 10 people at once using Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"people":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of people to enrich (max 10)"},"reveal_personal_emails":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal personal email addresses (uses credits)"},"reveal_phone_number":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal phone numbers (uses credits, requires webhook_url)"},"webhook_url":{"type":"string","required":false,"visibility":"user-only","description":"Webhook URL for async phone number delivery (required when reveal_phone_number is true)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_people_enrich":{"id":"apollo_people_enrich","name":"Apollo People Enrichment","description":"Enrich data for a single person using Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"first_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"First name of the person"},"last_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Last name of the person"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Full name of the person (alternative to first_name/last_name)"},"id":{"type":"string","required":false,"visibility":"user-or-llm","description":"Apollo ID for the person"},"hashed_email":{"type":"string","required":false,"visibility":"user-or-llm","description":"MD5 or SHA-256 hashed email"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Email address of the person"},"organization_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company name where the person works"},"domain":{"type":"string","required":false,"visibility":"user-or-llm","description":"Company domain (e.g., \\"apollo.io\\", \\"acme.com\\")"},"linkedin_url":{"type":"string","required":false,"visibility":"user-or-llm","description":"LinkedIn profile URL"},"reveal_personal_emails":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal personal email addresses (uses credits)"},"reveal_phone_number":{"type":"boolean","required":false,"visibility":"user-only","description":"Reveal phone numbers (uses credits, requires webhook_url)"},"webhook_url":{"type":"string","required":false,"visibility":"user-only","description":"Webhook URL for async phone number delivery (required when reveal_phone_number is true)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_people_search":{"id":"apollo_people_search","name":"Apollo People Search","description":"Search Apollo\'s database for people using demographic filters","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key"},"person_titles":{"type":"array","required":false,"visibility":"user-or-llm","description":"Job titles to search for (e.g., [\\"CEO\\", \\"VP of Sales\\"])"},"include_similar_titles":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Whether to return people with job titles similar to person_titles"},"person_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Locations to search in (e.g., [\\"San Francisco, CA\\", \\"New York, NY\\"])"},"person_seniorities":{"type":"array","required":false,"visibility":"user-or-llm","description":"Seniority levels (one of: owner, founder, c_suite, partner, vp, head, director, manager, senior, entry, intern)"},"organization_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Apollo organization IDs to filter by (e.g., [\\"5e66b6381e05b4008c8331b8\\"])"},"organization_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Company names to search within (legacy filter)"},"organization_locations":{"type":"array","required":false,"visibility":"user-or-llm","description":"Headquarters locations of the people\'s current employer (e.g., [\'texas\', \'tokyo\', \'spain\'])"},"q_organization_domains_list":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employer domain names (e.g., [\\"apollo.io\\", \\"microsoft.com\\"]) — up to 1,000, no www. or @"},"organization_num_employees_ranges":{"type":"array","required":false,"visibility":"user-or-llm","description":"Employee count ranges for the person\'s current employer. Each entry is \\"min,max\\" (e.g., [\\"1,10\\", \\"250,500\\", \\"10000,20000\\"])"},"contact_email_status":{"type":"array","required":false,"visibility":"user-or-llm","description":"Email statuses to filter by: \\"verified\\", \\"unverified\\", \\"likely to engage\\", \\"unavailable\\""},"q_keywords":{"type":"string","required":false,"visibility":"user-or-llm","description":"Keywords to search for"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination, default 1 (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, default 25, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_sequence_add_contacts":{"id":"apollo_sequence_add_contacts","name":"Apollo Add Contacts to Sequence","description":"Add contacts to an Apollo sequence","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"sequence_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the sequence to add contacts to (e.g., \\"seq_abc123\\")"},"contact_ids":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of contact IDs to add to the sequence (e.g., [\\"con_abc123\\", \\"con_def456\\"]). Either contact_ids or label_names must be provided."},"label_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of label names to identify contacts to add to the sequence. Either contact_ids or label_names must be provided."},"send_email_from_email_account_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the email account to send from. Use the Get Email Accounts operation to look this up."},"send_email_from_email_address":{"type":"string","required":false,"visibility":"user-only","description":"Specific email address to send from within the email account."},"sequence_no_email":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if they have no email address"},"sequence_unverified_email":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts with unverified email addresses"},"sequence_job_change":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts who recently changed jobs"},"sequence_active_in_other_campaigns":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts active in other campaigns"},"sequence_finished_in_other_campaigns":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts who finished other campaigns"},"sequence_same_company_in_same_campaign":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if others from the same company are in the sequence"},"contacts_without_ownership_permission":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts without ownership permission"},"add_if_in_queue":{"type":"boolean","required":false,"visibility":"user-only","description":"Add contacts even if they are in the queue"},"contact_verification_skipped":{"type":"boolean","required":false,"visibility":"user-only","description":"Skip contact verification when adding"},"user_id":{"type":"string","required":false,"visibility":"user-only","description":"ID of the user performing the action"},"status":{"type":"string","required":false,"visibility":"user-only","description":"Initial status for added contacts: \\"active\\" or \\"paused\\""},"auto_unpause_at":{"type":"string","required":false,"visibility":"user-only","description":"ISO 8601 datetime to automatically unpause contacts"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_sequence_search":{"id":"apollo_sequence_search","name":"Apollo Search Sequences","description":"Search for sequences/campaigns in your team\'s Apollo account (master key required)","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"q_name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Search sequences by name (e.g., \\"Outbound Q1\\", \\"Follow-up\\")"},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_task_create":{"id":"apollo_task_create","name":"Apollo Create Task","description":"Create one or more tasks in Apollo (one task per contact_id, master key required)","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"user_id":{"type":"string","required":true,"visibility":"user-or-llm","description":"ID of the Apollo user the task is assigned to"},"contact_ids":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of contact IDs. One task is created per contact."},"priority":{"type":"string","required":false,"visibility":"user-or-llm","description":"Task priority: \\"high\\", \\"medium\\", or \\"low\\" (defaults to \\"medium\\")"},"due_at":{"type":"string","required":true,"visibility":"user-or-llm","description":"Due date/time in ISO 8601 format (e.g., \\"2024-12-31T23:59:59Z\\")"},"type":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task type: \\"call\\", \\"outreach_manual_email\\", \\"linkedin_step_connect\\", \\"linkedin_step_message\\", \\"linkedin_step_view_profile\\", \\"linkedin_step_interact_post\\", or \\"action_item\\""},"status":{"type":"string","required":true,"visibility":"user-or-llm","description":"Task status: \\"scheduled\\", \\"completed\\", or \\"skipped\\""},"note":{"type":"string","required":false,"visibility":"user-or-llm","description":"Free-form note providing context for the task"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"apollo_task_search":{"id":"apollo_task_search","name":"Apollo Search Tasks","description":"Search for tasks in Apollo","version":"1.0.0","params":{"credentialType":{"type":"string","required":false,"visibility":"hidden","description":"Stored credential type supplied by credential resolution"},"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Apollo API key (master key required)"},"sort_by_field":{"type":"string","required":false,"visibility":"user-or-llm","description":"Sort field: \\"task_due_at\\" or \\"task_priority\\""},"open_factor_names":{"type":"array","required":false,"visibility":"user-or-llm","description":"Filter by status. Common values: [\\"task_types\\"] for open tasks, [\\"task_completed_at\\"] for completed tasks."},"page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Page number for pagination (e.g., 1, 2, 3)"},"per_page":{"type":"number","required":false,"visibility":"user-or-llm","description":"Results per page, max 100 (e.g., 25, 50, 100)"}},"oauth":{"required":false,"provider":"apollo","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"],"authoritativeParams":["credentialType"]},"hostedApiKey":"none"},"appconfig_create_application":{"id":"appconfig_create_application","name":"AppConfig Create Application","description":"Create an application in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the application to create"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the application"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_configuration_profile":{"id":"appconfig_create_configuration_profile","name":"AppConfig Create Configuration Profile","description":"Create a configuration profile in an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to create the configuration profile in"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the configuration profile"},"locationUri":{"type":"string","required":true,"visibility":"user-or-llm","description":"Where the configuration is stored. Use \\"hosted\\" for AppConfig-hosted configurations, or an SSM/S3 URI"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the configuration profile"},"retrievalRoleArn":{"type":"string","required":false,"visibility":"user-or-llm","description":"ARN of an IAM role to retrieve the configuration (required for non-hosted URIs)"},"type":{"type":"string","required":false,"visibility":"user-or-llm","description":"Profile type: AWS.Freeform (default) or AWS.AppConfig.FeatureFlags"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_environment":{"id":"appconfig_create_environment","name":"AppConfig Create Environment","description":"Create an environment for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to create the environment in"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the environment to create"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the environment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_create_hosted_configuration_version":{"id":"appconfig_create_hosted_configuration_version","name":"AppConfig Create Hosted Configuration Version","description":"Create a new hosted configuration version for an AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to add the version to"},"content":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration content (e.g., a JSON or YAML document)"},"contentType":{"type":"string","required":true,"visibility":"user-or-llm","description":"Content type of the configuration (e.g., application/json, text/plain)"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the configuration version"},"latestVersionNumber":{"type":"number","required":false,"visibility":"user-or-llm","description":"The version number of the latest version, used for optimistic concurrency"},"versionLabel":{"type":"string","required":false,"visibility":"user-or-llm","description":"A user-defined label for the configuration version"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_application":{"id":"appconfig_delete_application","name":"AppConfig Delete Application","description":"Delete an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_configuration_profile":{"id":"appconfig_delete_configuration_profile","name":"AppConfig Delete Configuration Profile","description":"Delete an AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_environment":{"id":"appconfig_delete_environment","name":"AppConfig Delete Environment","description":"Delete an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_delete_hosted_configuration_version":{"id":"appconfig_delete_hosted_configuration_version","name":"AppConfig Delete Hosted Configuration Version","description":"Delete a specific hosted configuration version from an AppConfig profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID that owns the version"},"versionNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The version number to delete"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_application":{"id":"appconfig_get_application","name":"AppConfig Get Application","description":"Get details about a single AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_configuration":{"id":"appconfig_get_configuration","name":"AppConfig Get Configuration","description":"Retrieve the latest deployed configuration for an AppConfig application, environment, and profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID or name to retrieve configuration for"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID or name to retrieve configuration for"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID or name to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_configuration_profile":{"id":"appconfig_get_configuration_profile","name":"AppConfig Get Configuration Profile","description":"Get details about a single AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_deployment":{"id":"appconfig_get_deployment","name":"AppConfig Get Deployment","description":"Get details about a specific AWS AppConfig deployment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployment"},"deploymentNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The sequence number of the deployment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_environment":{"id":"appconfig_get_environment","name":"AppConfig Get Environment","description":"Get details about a single AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_get_hosted_configuration_version":{"id":"appconfig_get_hosted_configuration_version","name":"AppConfig Get Hosted Configuration Version","description":"Retrieve a specific hosted configuration version from an AppConfig profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to read the version from"},"versionNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The version number to retrieve"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_applications":{"id":"appconfig_list_applications","name":"AppConfig List Applications","description":"List applications in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of applications to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_configuration_profiles":{"id":"appconfig_list_configuration_profiles","name":"AppConfig List Configuration Profiles","description":"List configuration profiles for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profiles"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of configuration profiles to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_deployment_strategies":{"id":"appconfig_list_deployment_strategies","name":"AppConfig List Deployment Strategies","description":"List deployment strategies available in AWS AppConfig","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of deployment strategies to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_deployments":{"id":"appconfig_list_deployments","name":"AppConfig List Deployments","description":"List deployments for an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployments"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployments"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of deployments to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_environments":{"id":"appconfig_list_environments","name":"AppConfig List Environments","description":"List environments for an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environments"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of environments to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_list_hosted_configuration_versions":{"id":"appconfig_list_hosted_configuration_versions","name":"AppConfig List Hosted Configuration Versions","description":"List hosted configuration versions for an AWS AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to list versions for"},"maxResults":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of versions to return (1-50)"},"nextToken":{"type":"string","required":false,"visibility":"user-or-llm","description":"Pagination token from a previous response"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_start_deployment":{"id":"appconfig_start_deployment","name":"AppConfig Start Deployment","description":"Start deploying a configuration version to an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to deploy in"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to deploy to"},"deploymentStrategyId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The deployment strategy ID to use"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to deploy"},"configurationVersion":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration version to deploy"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"Description of the deployment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_stop_deployment":{"id":"appconfig_stop_deployment","name":"AppConfig Stop Deployment","description":"Stop an in-progress AWS AppConfig deployment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID of the deployment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID of the deployment"},"deploymentNumber":{"type":"number","required":true,"visibility":"user-or-llm","description":"The sequence number of the deployment to stop"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_application":{"id":"appconfig_update_application","name":"AppConfig Update Application","description":"Update the name or description of an AWS AppConfig application","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the application"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the application"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_configuration_profile":{"id":"appconfig_update_configuration_profile","name":"AppConfig Update Configuration Profile","description":"Update the name, description, or retrieval role of an AppConfig configuration profile","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the configuration profile"},"configurationProfileId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The configuration profile ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the configuration profile"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the configuration profile"},"retrievalRoleArn":{"type":"string","required":false,"visibility":"user-or-llm","description":"New ARN of the IAM role used to retrieve the configuration"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"appconfig_update_environment":{"id":"appconfig_update_environment","name":"AppConfig Update Environment","description":"Update the name or description of an AWS AppConfig environment","version":"1.0","params":{"region":{"type":"string","required":true,"visibility":"user-only","description":"AWS region (e.g., us-east-1)"},"accessKeyId":{"type":"string","required":true,"visibility":"user-only","description":"AWS access key ID"},"secretAccessKey":{"type":"string","required":true,"visibility":"user-only","description":"AWS secret access key"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The application ID that owns the environment"},"environmentId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The environment ID to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"New name for the environment"},"description":{"type":"string","required":false,"visibility":"user-or-llm","description":"New description for the environment"}},"oauth":{"required":false,"provider":"aws","credentialKind":"service-account","credentialSuppliedParams":["accessKeyId","secretAccessKey","awsAccessKeyId","awsSecretAccessKey"]},"hostedApiKey":"none"},"arxiv_get_author_papers":{"id":"arxiv_get_author_papers","name":"ArXiv Get Author Papers","description":"Search for papers by a specific author on ArXiv.","version":"1.0.0","params":{"authorName":{"type":"string","required":true,"visibility":"user-or-llm","description":"Author name to search for"},"maxResults":{"type":"number","required":false,"visibility":"user-only","description":"Maximum number of results to return (default: 10, max: 2000)"}},"hostedApiKey":"none"},"arxiv_get_paper":{"id":"arxiv_get_paper","name":"ArXiv Get Paper","description":"Get detailed information about a specific ArXiv paper by its ID.","version":"1.0.0","params":{"paperId":{"type":"string","required":true,"visibility":"user-or-llm","description":"ArXiv paper ID (e.g., \\"1706.03762\\")"}},"hostedApiKey":"none"},"arxiv_search":{"id":"arxiv_search","name":"ArXiv Search","description":"Search for academic papers on ArXiv by keywords, authors, titles, or other fields.","version":"1.0.0","params":{"searchQuery":{"type":"string","required":true,"visibility":"user-or-llm","description":"The search query to execute"},"searchField":{"type":"string","required":false,"visibility":"user-only","description":"Field to search in: all, ti (title), au (author), abs (abstract), co (comment), jr (journal), cat (category), rn (report number)"},"maxResults":{"type":"number","required":false,"visibility":"user-only","description":"Maximum number of results to return (default: 10, max: 2000)"},"sortBy":{"type":"string","required":false,"visibility":"user-only","description":"Sort by: relevance, lastUpdatedDate, submittedDate (default: relevance)"},"sortOrder":{"type":"string","required":false,"visibility":"user-only","description":"Sort order: ascending, descending (default: descending)"}},"hostedApiKey":"none"},"asana_add_comment":{"id":"asana_add_comment","name":"Asana Add Comment","description":"Add a comment (story) to an Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana task GID (numeric string)"},"text":{"type":"string","required":true,"visibility":"user-or-llm","description":"The text content of the comment"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_add_followers":{"id":"asana_add_followers","name":"Asana Add Followers","description":"Add one or more followers to an Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana task (numeric string)"},"followers":{"type":"array","required":true,"visibility":"user-or-llm","description":"Array of user GIDs to add as followers to the task"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_project":{"id":"asana_create_project","name":"Asana Create Project","description":"Create a new project in an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) where the project will be created"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the project"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the project"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_section":{"id":"asana_create_section","name":"Asana Create Section","description":"Create a new section in an Asana project","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana project (numeric string) to add the section to"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the section"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_subtask":{"id":"asana_create_subtask","name":"Asana Create Subtask","description":"Create a subtask under an existing Asana task","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the parent Asana task (numeric string)"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the subtask"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the subtask"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"User GID to assign the subtask to"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_create_task":{"id":"asana_create_task","name":"Asana Create Task","description":"Create a new task in Asana","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) where the task will be created"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"Name of the task"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Notes or description for the task"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"User GID to assign the task to"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_delete_task":{"id":"asana_delete_task","name":"Asana Delete Task","description":"Delete an Asana task by its GID (moves it to the trash)","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana task to delete (numeric string)"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_project":{"id":"asana_get_project","name":"Asana Get Project","description":"Retrieve a single Asana project by its GID","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana project GID (numeric string) to retrieve"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_projects":{"id":"asana_get_projects","name":"Asana Get Projects","description":"Retrieve all projects from an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to retrieve projects from"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_get_task":{"id":"asana_get_task","name":"Asana Get Task","description":"Retrieve a single task by GID or get multiple tasks with filters","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":false,"visibility":"user-or-llm","description":"The globally unique identifier (GID) of the task. If not provided, will get multiple tasks."},"workspace":{"type":"string","required":false,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to filter tasks (required when not using taskGid)"},"project":{"type":"string","required":false,"visibility":"user-or-llm","description":"Asana project GID (numeric string) to filter tasks"},"limit":{"type":"number","required":false,"visibility":"user-or-llm","description":"Maximum number of tasks to return (default: 50)"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_list_sections":{"id":"asana_list_sections","name":"Asana List Sections","description":"List all sections in an Asana project","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"projectGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"GID of the Asana project (numeric string) to list sections from"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_list_workspaces":{"id":"asana_list_workspaces","name":"Asana List Workspaces","description":"List all Asana workspaces and organizations the authenticated user belongs to","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_search_tasks":{"id":"asana_search_tasks","name":"Asana Search Tasks","description":"Search for tasks in an Asana workspace","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"workspace":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana workspace GID (numeric string) to search tasks in"},"text":{"type":"string","required":false,"visibility":"user-or-llm","description":"Text to search for in task names"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"Filter tasks by assignee user GID"},"projects":{"type":"array","required":false,"visibility":"user-or-llm","description":"Array of Asana project GIDs (numeric strings) to filter tasks by"},"completed":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Filter by completion status"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"asana_update_task":{"id":"asana_update_task","name":"Asana Update Task","description":"Update an existing task in Asana","version":"1.0.0","params":{"accessToken":{"type":"string","required":true,"visibility":"hidden","description":"OAuth access token for Asana"},"taskGid":{"type":"string","required":true,"visibility":"user-or-llm","description":"Asana task GID (numeric string) of the task to update"},"name":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated name for the task"},"notes":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated notes or description for the task"},"assignee":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated assignee user GID"},"completed":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Mark task as completed or not completed"},"due_on":{"type":"string","required":false,"visibility":"user-or-llm","description":"Updated due date in YYYY-MM-DD format"}},"oauth":{"required":true,"provider":"asana"},"hostedApiKey":"none"},"ashby_add_candidate_tag":{"id":"ashby_add_candidate_tag","name":"Ashby Add Candidate Tag","description":"Adds a tag to a candidate in Ashby and returns the updated candidate.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to add the tag to"},"tagId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the tag to add"}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_anonymize_candidate":{"id":"ashby_anonymize_candidate","name":"Ashby Anonymize Candidate","description":"Remove personally identifiable information from an Ashby candidate while retaining the candidate record and its applications. This operation does not delete the candidate. Requires the candidatesWrite permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"UUID of the candidate to anonymize"}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_change_application_source":{"id":"ashby_change_application_source","name":"Ashby Change Application Source","description":"Changes the source attributed to an existing application, so programmatically created applications report correctly on the recruiting side. Requires the candidatesWrite permission.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"UUID of the application whose source should change"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to attribute the application to, as returned by List Sources. Omit only when unsetSource is true."},"unsetSource":{"type":"boolean","required":false,"visibility":"user-or-llm","description":"Set true to deliberately clear the application source. Required to unset, so that a missing or empty sourceId cannot wipe attribution by accident."}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_change_application_stage":{"id":"ashby_change_application_stage","name":"Ashby Change Application Stage","description":"Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"applicationId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the application to update the stage of"},"interviewStageId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the interview stage to move the application to"},"archiveReasonId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Archive reason UUID. Required when moving to an Archived stage, ignored otherwise"},"archiveEmail":{"type":"json","required":false,"visibility":"user-or-llm","description":"Archive email configuration with communicationTemplateId and optional sendAt ISO 8601 timestamp. Pass null or omit to send no archive email."}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_create_application":{"id":"ashby_create_application","name":"Ashby Create Application","description":"Creates a new application for a candidate on a job. Optionally specify interview plan, stage, source, and credited user.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to consider for the job"},"jobId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the job to consider the candidate for"},"interviewPlanId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the interview plan to use (defaults to the job default plan)"},"interviewStageId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the interview stage to place the application in, or FirstPreInterviewScreen (defaults to the first Lead stage)"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to set on the application"},"creditedToUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the user the application is credited to"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"ISO 8601 timestamp to set as the application creation date (defaults to now)"},"applicationHistory":{"type":"json","required":false,"visibility":"user-or-llm","description":"Optional documented application history entries to create with the application"}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_create_candidate":{"id":"ashby_create_candidate","name":"Ashby Create Candidate","description":"Creates a new candidate record in Ashby.","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"name":{"type":"string","required":true,"visibility":"user-or-llm","description":"The candidate full name"},"email":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary email address for the candidate"},"phoneNumber":{"type":"string","required":false,"visibility":"user-or-llm","description":"Primary phone number for the candidate"},"linkedInUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"LinkedIn profile URL"},"githubUrl":{"type":"string","required":false,"visibility":"user-or-llm","description":"GitHub profile URL"},"website":{"type":"string","required":false,"visibility":"user-or-llm","description":"Personal website URL"},"sourceId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the source to attribute the candidate to"},"creditedToUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"UUID of the Ashby user to credit with sourcing this candidate"},"createdAt":{"type":"string","required":false,"visibility":"user-or-llm","description":"Backdated creation timestamp in ISO 8601 (e.g. 2024-01-01T00:00:00Z). Defaults to now."},"alternateEmailAddresses":{"type":"json","required":false,"visibility":"user-or-llm","description":"Array of additional email address strings to add to the candidate, e.g. [\\"a@x.com\\",\\"b@y.com\\"]"},"location":{"type":"json","required":false,"visibility":"user-or-llm","description":"Candidate location object with optional city, region, and country"}},"oauth":{"required":false,"provider":"ashby","credentialKind":"service-account","credentialParams":{"apiKey":"accessToken"},"credentialSuppliedParams":["apiKey"]},"hostedApiKey":"none"},"ashby_create_note":{"id":"ashby_create_note","name":"Ashby Create Note","description":"Creates a note on a candidate in Ashby. Supports plain text and HTML content (bold, italic, underline, links, lists, code).","version":"1.0.0","params":{"apiKey":{"type":"string","required":true,"visibility":"user-only","description":"Ashby API Key"},"onBehalfOfUserId":{"type":"string","required":false,"visibility":"user-or-llm","description":"Active Ashby user UUID to attribute this mutation to; the API key must permit on-behalf-of calls"},"candidateId":{"type":"string","required":true,"visibility":"user-or-llm","description":"The UUID of the candidate to add the note to"},"note":{"type":"string","required":true,"visibility":"user-or-llm","description":"The note content. If noteType is text/html, supports: , , , ,