@@ -923,6 +953,305 @@ describe('collectSnapshot', () => {
})
})
+describe('semantic control state', () => {
+ it('scopes a fresh snapshot to one card without reading sibling geometry', () => {
+ document.body.innerHTML =
+ '
Save card
Outside card '
+ for (const element of document.querySelectorAll('*')) visible(element)
+ const full = outlineOf(collectSnapshot())
+ const oldRootRef = refFor(full, 'Selected card')
+ const outside = document.querySelector('body > button')!
+ const outsideGeometry = vi.spyOn(outside, 'getBoundingClientRect')
+
+ const scoped = runSerialized(collectSnapshot, [100, oldRootRef]) as {
+ scoped: boolean
+ outline: string
+ refIds: number[]
+ }
+ expect(scoped.scoped).toBe(true)
+ expect(scoped.outline).toContain('Selected card')
+ expect(scoped.outline).toContain('Save card')
+ expect(scoped.outline).not.toContain('Outside card')
+ expect(scoped.outline).not.toContain('private')
+ expect(scoped.refIds.every((id) => id >= 100)).toBe(true)
+ expect(window.__simAgentResolveElement?.(oldRootRef)).toBeNull()
+ expect(outsideGeometry).not.toHaveBeenCalled()
+ })
+
+ it('rejects stale or framed snapshot roots without replacing the page registry', () => {
+ const button = document.createElement('button')
+ document.body.append(visible(button))
+ register(button)
+ button.remove()
+ expect(collectSnapshot(10, 0)).toMatchObject({ error: 'stale' })
+ expect(window.__simAgentElements?.[0]).toBe(button)
+
+ const frame = document.createElement('iframe')
+ document.body.append(frame)
+ const child = frame.contentDocument!.createElement('button')
+ frame.contentDocument!.body.append(visible(child))
+ register(child)
+ expect(collectSnapshot(10, 0)).toEqual({ error: 'framed-snapshot' })
+ })
+
+ it.each(['detached', 'hidden', 'renamed'])(
+ 'rejects a %s root before scoped capture without adopting a lookalike',
+ (change) => {
+ document.body.innerHTML =
+ '
Save card
'
+ for (const element of document.querySelectorAll('*')) visible(element)
+ const root = document.querySelector('#card')!
+ const rootRef = refFor(outlineOf(collectSnapshot()), 'Selected card')
+ const replacement = root.cloneNode(true) as HTMLElement
+ for (const element of [replacement, ...replacement.querySelectorAll('*')]) visible(element)
+ if (change === 'detached') root.replaceWith(replacement)
+ else {
+ root.after(replacement)
+ if (change === 'hidden') root.setAttribute('hidden', '')
+ else root.setAttribute('aria-label', 'Different card')
+ }
+
+ expect(runSerialized(collectSnapshot, [100, rootRef])).toMatchObject({ error: 'stale' })
+ expect(window.__simAgentElements?.[rootRef]).toBe(root)
+ expect(runSerialized(collectSnapshot, [100, rootRef])).toMatchObject({ error: 'stale' })
+ }
+ )
+
+ it('marks unreadable scoped frame content truncated', () => {
+ const root = visible(document.createElement('div'))
+ const frame = visible(document.createElement('iframe'))
+ root.append(frame)
+ document.body.append(root)
+ register(root)
+ Object.defineProperty(frame, 'contentDocument', { value: null })
+ expect(collectSnapshot(10, 0)).toMatchObject({ scoped: true, truncated: true })
+ })
+
+ it('does not recover scoped refs into a different card after the original closes', () => {
+ document.body.innerHTML =
+ '
Save card
'
+ for (const element of document.querySelectorAll('*')) visible(element)
+ const root = document.querySelector('body > div')!
+ const rootRef = refFor(outlineOf(collectSnapshot()), 'Selected card')
+ const saveRef = refFor(outlineOf(collectSnapshot(10, rootRef)), 'Save card')
+ const replacement = root.cloneNode(true) as HTMLElement
+ for (const element of [replacement, ...replacement.querySelectorAll('*')]) visible(element)
+ root.replaceWith(replacement)
+
+ expect(window.__simAgentResolveElement?.(saveRef)).toBeNull()
+ expect(window.__simAgentStaleReason).toContain('scoped snapshot root')
+ })
+
+ it('keeps scoped snapshots bounded when a selected container is very large', () => {
+ const root = document.createElement('div')
+ root.tabIndex = 0
+ root.setAttribute('aria-label', 'Large card')
+ document.body.append(visible(root))
+ register(root)
+ for (let index = 0; index < 400; index++) {
+ const button = visible(document.createElement('button'))
+ button.textContent = `Action ${index}`
+ root.append(button)
+ }
+ const scoped = collectSnapshot(10, 0) as { refIds: number[]; truncated: boolean }
+ expect(scoped.refIds).toHaveLength(300)
+ expect(scoped.truncated).toBe(true)
+ })
+
+ it('distinguishes hidden registered nodes from detached nodes without action recovery', () => {
+ const button = visible(document.createElement('button'))
+ document.body.append(button)
+ register(button)
+ window.__simAgentResolveElement = vi.fn(() => null)
+ button.style.display = 'none'
+
+ expect(readPageActionState(false, 0, 'registered')).toMatchObject({
+ targetState: { present: true, rendered: false },
+ })
+ button.remove()
+ expect(readPageActionState(false, 0, 'registered')).toMatchObject({
+ targetState: { present: false, rendered: false },
+ })
+ expect(window.__simAgentResolveElement).not.toHaveBeenCalled()
+ })
+
+ it('does not report a text input as an unchecked control', () => {
+ const input = visible(document.createElement('input'))
+ document.body.append(input)
+ register(input)
+
+ expect(readPageActionState(false, 0, 'registered')).toMatchObject({
+ targetState: { present: true, checked: undefined },
+ })
+ expect(readPageActionState(false, 1, 'registered')).toEqual({ error: 'stale' })
+ })
+
+ it.each([true, false])(
+ 'reports native disclosure state as %s without inventing it on other elements',
+ (open) => {
+ document.body.innerHTML =
+ '
Details Other '
+ const details = document.querySelector('details')!
+ const dialog = document.querySelector('dialog')!
+ details.open = open
+ dialog.open = open
+ const elements = [
+ details,
+ document.querySelector('summary')!,
+ dialog,
+ document.querySelector('button')!,
+ ]
+ register(...elements.map(visible))
+ for (let id = 0; id < elements.length; id++) {
+ expect(runSerialized(readPageActionState, [false, id, 'registered'])).toMatchObject({
+ targetState: { open: id === 3 ? undefined : open },
+ })
+ }
+ }
+ )
+
+ it.each(['checkbox', 'radio'])('reads native %s state with XHTML tag casing', (type) => {
+ const input = visible(document.createElement('input'))
+ input.type = type
+ input.checked = true
+ Object.defineProperty(input, 'tagName', { value: 'input' })
+ document.body.append(input)
+ register(input)
+
+ expect(runSerialized(readPageActionState, [false, 0, 'registered'])).toMatchObject({
+ targetState: { checked: true },
+ })
+ })
+
+ it('preserves native and ARIA mixed states instead of reporting unchecked', () => {
+ document.body.innerHTML =
+ '
'
+ const checkbox = visible(document.querySelector('input') as HTMLInputElement)
+ checkbox.indeterminate = true
+ register(checkbox, visible(document.querySelector('div') as HTMLDivElement))
+
+ expect(readCheckableElementState(0)).toMatchObject({ checked: 'mixed' })
+ expect(readCheckableElementState(1)).toMatchObject({ checked: 'mixed' })
+ })
+
+ it('honors disabled fieldsets and ARIA-disabled ancestors', () => {
+ document.body.innerHTML =
+ '
'
+ register(
+ visible(document.querySelector('input') as HTMLInputElement),
+ visible(document.querySelector('button') as HTMLButtonElement)
+ )
+
+ expect(readCheckableElementState(0)).toMatchObject({ disabled: true })
+ expect(readCheckableElementState(1)).toMatchObject({ disabled: true })
+ })
+
+ it.each([true, false])(
+ 'reads ARIA-disabled=%s across shadow boundaries for waits and checked state',
+ (disabled) => {
+ const host = visible(document.createElement('div'))
+ host.setAttribute('aria-disabled', String(disabled))
+ const nestedHost = visible(document.createElement('div'))
+ host.attachShadow({ mode: 'open' }).append(nestedHost)
+ const checkbox = visible(document.createElement('input'))
+ checkbox.type = 'checkbox'
+ nestedHost.attachShadow({ mode: 'open' }).append(checkbox)
+ document.body.append(host)
+ register(checkbox)
+
+ expect(runSerialized(readCheckableElementState, [0])).toMatchObject({ disabled })
+ expect(runSerialized(readPageActionState, [false, 0, 'registered'])).toMatchObject({
+ targetState: { disabled },
+ })
+ }
+ )
+
+ it('reads native and ARIA checkable controls without mutating them', () => {
+ document.body.innerHTML = `
+
+
Alerts
+ `
+ const checkbox = visible(document.querySelector('input') as HTMLInputElement)
+ const toggle = visible(document.querySelector('button') as HTMLButtonElement)
+ register(checkbox, toggle)
+
+ expect(readCheckableElementState(0)).toMatchObject({
+ checked: true,
+ disabled: false,
+ kind: 'input:checkbox',
+ })
+ expect(readCheckableElementState(1)).toMatchObject({
+ checked: false,
+ disabled: true,
+ kind: 'role:switch',
+ })
+ expect(checkbox.checked).toBe(true)
+ })
+
+ it('returns a viewport-clamped element screenshot rectangle', () => {
+ const button = visible(document.createElement('button'))
+ button.scrollIntoView = vi.fn()
+ button.getBoundingClientRect = () =>
+ ({
+ x: -10,
+ y: 5,
+ width: 120,
+ height: 20,
+ top: 5,
+ left: -10,
+ right: 110,
+ bottom: 25,
+ }) as DOMRect
+ document.body.append(button)
+ register(button)
+
+ expect(getElementScreenshotRect(0)).toMatchObject({
+ x: 0,
+ y: 5,
+ width: 110,
+ height: 20,
+ element: 'button',
+ })
+ expect(button.scrollIntoView).not.toHaveBeenCalled()
+ })
+
+ it('rejects a replacement screenshot target even when its geometry matches', () => {
+ const button = visible(document.createElement('button'))
+ button.id = 'save'
+ button.textContent = 'Save'
+ document.body.append(button)
+ const ref = refFor(outlineOf(collectSnapshot()), 'Save')
+ expect(getElementScreenshotRect(ref)).toMatchObject({ width: 100, height: 20 })
+ button.replaceWith(visible(button.cloneNode(true) as HTMLElement))
+
+ expect(runSerialized(getElementScreenshotRect, [ref])).toMatchObject({ error: 'stale' })
+ expect(window.__simAgentElements?.[ref]).toBe(button)
+ })
+
+ it('rejects same-origin frame crops rather than using frame-local coordinates', () => {
+ const frame = document.createElement('iframe')
+ document.body.append(frame)
+ const button = frame.contentDocument!.createElement('button')
+ frame.contentDocument!.body.append(button)
+ register(visible(button))
+
+ expect(getElementScreenshotRect(0)).toEqual({ error: 'framed-screenshot' })
+ expect(readPageActionState(false, 0, 'registered')).toEqual({ error: 'framed-wait' })
+ })
+
+ it('does not scroll an offscreen element into view for a screenshot', () => {
+ const button = visible(document.createElement('button'))
+ button.scrollIntoView = vi.fn()
+ document.body.append(button)
+ button.getBoundingClientRect = () =>
+ ({ left: 0, right: 20, top: 5000, bottom: 5020, width: 20, height: 20 }) as DOMRect
+ register(button)
+
+ expect(getElementScreenshotRect(0)).toEqual({ error: 'not-visible' })
+ expect(button.scrollIntoView).not.toHaveBeenCalled()
+ })
+})
+
describe('scrollPage', () => {
function makeScroller(scrollTop: number): {
scroller: HTMLDivElement
diff --git a/apps/desktop/src/main/browser-agent/page-functions.ts b/apps/desktop/src/main/browser-agent/page-functions.ts
index e9924381d9c..2bcd8fc3198 100644
--- a/apps/desktop/src/main/browser-agent/page-functions.ts
+++ b/apps/desktop/src/main/browser-agent/page-functions.ts
@@ -29,7 +29,10 @@
declare global {
interface Window {
__simAgentElements?: Element[]
- __simAgentResolveElement?: (id: number) => { element: Element; recovered: boolean } | null
+ __simAgentResolveElement?: (
+ id: number,
+ allowRecovery?: boolean
+ ) => { element: Element; recovered: boolean } | null
__simAgentMutationStates?: Array<{
root: Node
observer: MutationObserver
@@ -50,7 +53,18 @@ declare global {
* interactive elements carrying numeric ids, walking open shadow roots and
* same-origin iframes. Rebuilds the element registry as a side effect.
*/
-export function collectSnapshot(startingElementId = 0): unknown {
+export function collectSnapshot(startingElementId = 0, elementId?: number): unknown {
+ const resolver = window.__simAgentResolveElement
+ const scopedRoot =
+ elementId === undefined
+ ? undefined
+ : resolver
+ ? resolver(elementId, false)?.element
+ : window.__simAgentElements?.[elementId]
+ if (elementId !== undefined) {
+ if (!scopedRoot?.isConnected) return { error: 'stale', reason: window.__simAgentStaleReason }
+ if (scopedRoot.ownerDocument !== document) return { error: 'framed-snapshot' }
+ }
const refCap = 300
const lineCap = 600
const nodeCap = 12_000
@@ -448,10 +462,13 @@ export function collectSnapshot(startingElementId = 0): unknown {
// like any other. Redaction above is realm-safe and runs first, so
// widening this cannot expose a credential field.
const value = (el as HTMLInputElement).value
- if (tag === 'INPUT' && (el as HTMLInputElement).type === 'file') {
+ const inputType = tag === 'INPUT' ? (el as HTMLInputElement).type : ''
+ if (inputType === 'file') {
parts.push('upload-unsupported')
- } else if (value && isSensitiveValueField(el)) parts.push('value-withheld')
- else if (value) parts.push(`value=${quote(cut(String(value), 120))}`)
+ } else if (inputType !== 'checkbox' && inputType !== 'radio') {
+ if (value && isSensitiveValueField(el)) parts.push('value-withheld')
+ else if (value) parts.push(`value=${quote(cut(String(value), 120))}`)
+ }
}
if (tag === 'A') {
const href = el.getAttribute('href')
@@ -459,7 +476,28 @@ export function collectSnapshot(startingElementId = 0): unknown {
}
if ((el as HTMLInputElement).disabled === true) parts.push('disabled')
if (el.getAttribute('aria-disabled') === 'true') parts.push('aria-disabled')
- if ((el as HTMLInputElement).checked === true) parts.push('checked')
+ if (el.getAttribute('aria-readonly') === 'true') parts.push('aria-readonly')
+ if (el.getAttribute('aria-required') === 'true') parts.push('aria-required')
+ if (tag === 'INPUT') {
+ const input = el as HTMLInputElement
+ if (input.type === 'checkbox' || input.type === 'radio') {
+ parts.push(input.indeterminate ? 'mixed' : input.checked ? 'checked' : 'unchecked')
+ }
+ if (input.readOnly) parts.push('readonly')
+ if (input.required) parts.push('required')
+ } else if (tag === 'TEXTAREA') {
+ const textarea = el as HTMLTextAreaElement
+ if (textarea.readOnly) parts.push('readonly')
+ if (textarea.required) parts.push('required')
+ } else if (tag === 'SELECT' && (el as HTMLSelectElement).required) {
+ parts.push('required')
+ }
+ for (const attribute of ['aria-checked', 'aria-expanded', 'aria-pressed', 'aria-selected']) {
+ const value = el.getAttribute(attribute)
+ if (value === 'true' || value === 'false' || value === 'mixed') {
+ parts.push(`${attribute}=${value}`)
+ }
+ }
const suffix = parts.length > 0 ? ` ${parts.join(' ')}` : ''
const lineIndex = lines.length
if (push(`${indent}- ${role} ${quote(name)} [ref=${id}]${suffix}`)) {
@@ -524,12 +562,12 @@ export function collectSnapshot(startingElementId = 0): unknown {
)
}
- const walk = (root: ParentNode, depth: number, suppressTextCoveredBy = ''): void => {
+ const walk = (elements: Iterable
, depth: number, suppressTextCoveredBy = ''): void => {
if (refCount >= refCap || depth > depthCap) {
truncated = true
return
}
- for (const el of Array.from(root.children)) {
+ for (const el of elements) {
visitedNodes++
if (refCount >= refCap || visitedNodes > nodeCap) {
truncated = true
@@ -582,21 +620,24 @@ export function collectSnapshot(startingElementId = 0): unknown {
const innerDoc = (el as HTMLIFrameElement).contentDocument
if (innerDoc?.body && isVisible(el)) {
if (!push(`${indent}- iframe:`)) return
- walk(innerDoc.body, childDepth + 1, coveredText)
+ walk(innerDoc.body.children, childDepth + 1, coveredText)
+ } else if (scopedRoot && !innerDoc && visible) {
+ truncated = true
}
} catch {
- // Cross-origin iframe — not readable.
+ if (scopedRoot && visible) truncated = true
}
continue
}
const shadow = (el as HTMLElement).shadowRoot
- if (shadow) walk(shadow, childDepth, coveredText)
- walk(el, childDepth, coveredText)
+ if (shadow) walk(shadow.children, childDepth, coveredText)
+ walk(el.children, childDepth, coveredText)
}
}
- if (document.body) walk(document.body, 0)
+ if (scopedRoot) walk([scopedRoot], 0)
+ else if (document.body) walk(document.body.children, 0)
/**
* React commonly replaces a control's DOM node while preserving its
@@ -604,7 +645,11 @@ export function collectSnapshot(startingElementId = 0): unknown {
* fingerprint still identify one candidate; a weak or ambiguous match is a
* real stale ref, never permission to click something nearby.
*/
- window.__simAgentResolveElement = (id: number) => {
+ window.__simAgentResolveElement = (id: number, allowRecovery = true) => {
+ if (scopedRoot && !scopedRoot.isConnected) {
+ window.__simAgentStaleReason = 'the scoped snapshot root left the DOM'
+ return null
+ }
const locator = locators[id]
if (!locator) {
window.__simAgentStaleReason = `id ${id} is not in the current snapshot's registry`
@@ -737,6 +782,11 @@ export function collectSnapshot(startingElementId = 0): unknown {
}
}
+ if (!allowRecovery) {
+ window.__simAgentStaleReason = 'the original snapshot node is detached or hidden'
+ return null
+ }
+
// Past this point the original node is gone or hidden, so anything returned
// is a DIFFERENT node adopted by structural resemblance. Identity matching
// compares origins only — deliberately, so a pushState between snapshot and
@@ -767,7 +817,7 @@ export function collectSnapshot(startingElementId = 0): unknown {
let candidateCount = 0
const collect = (root: ParentNode, depth = 0): void => {
if (depth > depthCap || candidateCount >= nodeCap) return
- for (const element of Array.from(root.children)) {
+ for (const element of root.children) {
candidateCount++
if (candidateCount > nodeCap) return
reachable.push(element)
@@ -785,7 +835,8 @@ export function collectSnapshot(startingElementId = 0): unknown {
collect(element, depth + 1)
}
}
- if (document.body) collect(document.body)
+ if (scopedRoot) collect(scopedRoot)
+ else if (document.body) collect(document.body)
const scored = reachable
.filter((candidate) => identityMatches(candidate) && isCurrentlyVisible(candidate))
@@ -862,6 +913,7 @@ export function collectSnapshot(startingElementId = 0): unknown {
url: cut(window.location.href, 4096),
title: cut(document.title, 500),
outline: lines.join('\n'),
+ ...(scopedRoot ? { scoped: true } : {}),
truncated,
scrollY: Math.round(window.scrollY),
pageHeight: Math.round(document.documentElement.scrollHeight),
@@ -2041,14 +2093,24 @@ export function pressKeyOnPage(
* Captures non-sensitive page state around a trusted input event. The driver
* compares two readings so “the event was dispatched” is never confused with
* “the page visibly reacted.”
+ * Registered-node waits return only target state, without action ref recovery
+ * or the broader page-effect scan.
*/
-export function readPageActionState(resetMutationRevision = false, elementId?: number): unknown {
+export function readPageActionState(
+ resetMutationRevision = false,
+ elementId?: number,
+ targetResolution: 'actionable' | 'registered' = 'actionable'
+): unknown {
const registeredElement =
typeof elementId === 'number' ? (window.__simAgentElements || [])[elementId] : undefined
const resolver = window.__simAgentResolveElement
+ if (targetResolution === 'registered') {
+ if (!registeredElement) return { error: 'stale' }
+ if (registeredElement.ownerDocument !== document) return { error: 'framed-wait' }
+ }
const resolved =
typeof elementId === 'number'
- ? resolver
+ ? resolver && targetResolution === 'actionable'
? resolver(elementId)
: registeredElement?.isConnected
? { element: registeredElement, recovered: false }
@@ -2058,6 +2120,89 @@ export function readPageActionState(resetMutationRevision = false, elementId?: n
const observedDocument =
observedElement?.ownerDocument ?? registeredElement?.ownerDocument ?? document
const observedWindow = observedDocument.defaultView ?? window
+ const isEffectivelyRendered = (element: Element): boolean => {
+ const rect = element.getBoundingClientRect()
+ const view = element.ownerDocument.defaultView
+ if (
+ !view ||
+ rect.width <= 1 ||
+ rect.height <= 1 ||
+ rect.right <= 0 ||
+ rect.bottom <= 0 ||
+ rect.left >= view.innerWidth ||
+ rect.top >= view.innerHeight
+ ) {
+ return false
+ }
+ for (let current: Element | null = element; current; ) {
+ const currentView: Window | null = current.ownerDocument.defaultView
+ const style = currentView?.getComputedStyle(current)
+ const opacity = Number.parseFloat(style?.opacity || '1')
+ if (
+ !style ||
+ style.display === 'none' ||
+ style.visibility === 'hidden' ||
+ style.contentVisibility === 'hidden' ||
+ (Number.isFinite(opacity) && opacity <= 0.01) ||
+ current.hasAttribute('hidden') ||
+ current.getAttribute('aria-hidden') === 'true'
+ ) {
+ return false
+ }
+ if (current.parentElement) current = current.parentElement
+ else {
+ const root = current.getRootNode()
+ current = 'host' in root ? (root.host as Element) : null
+ }
+ }
+ return true
+ }
+
+ let disabled = observedElement?.matches(':disabled') === true
+ for (let ancestor = observedElement; ancestor && !disabled; ) {
+ disabled = ancestor.getAttribute('aria-disabled') === 'true'
+ const root = ancestor.getRootNode()
+ ancestor = ancestor.parentElement ?? ('host' in root ? (root.host as Element) : null)
+ }
+ const observedTag = observedElement?.tagName.toUpperCase()
+ const disclosure =
+ observedTag === 'DETAILS' || observedTag === 'DIALOG'
+ ? observedElement
+ : observedTag === 'SUMMARY' &&
+ observedElement?.parentElement?.tagName.toUpperCase() === 'DETAILS'
+ ? observedElement.parentElement
+ : undefined
+ const targetState =
+ typeof elementId !== 'number'
+ ? undefined
+ : observedElement
+ ? {
+ present: true,
+ rendered: isEffectivelyRendered(observedElement),
+ ariaExpanded: observedElement.getAttribute('aria-expanded'),
+ ariaSelected: observedElement.getAttribute('aria-selected'),
+ ariaPressed: observedElement.getAttribute('aria-pressed'),
+ ariaChecked: observedElement.getAttribute('aria-checked'),
+ checked:
+ observedTag !== 'INPUT' ||
+ !['checkbox', 'radio'].includes((observedElement as HTMLInputElement).type)
+ ? undefined
+ : (observedElement as HTMLInputElement).indeterminate === true
+ ? 'mixed'
+ : Boolean((observedElement as HTMLInputElement).checked),
+ disabled,
+ selected:
+ 'selected' in observedElement
+ ? Boolean((observedElement as HTMLOptionElement).selected)
+ : undefined,
+ open: disclosure?.hasAttribute('open'),
+ hidden:
+ observedElement.hasAttribute('hidden') ||
+ observedElement.getAttribute('aria-hidden') === 'true',
+ }
+ : { present: false, rendered: false }
+ if (targetResolution === 'registered') return { targetState }
+
const observationRoot = observedDocument.body
const roots: ParentNode[] = observationRoot ? [observationRoot] : []
@@ -2242,70 +2387,6 @@ export function readPageActionState(resetMutationRevision = false, elementId?: n
.slice(0, 30)
.map((element) => `${element.tagName}:${Math.round((element as HTMLElement).scrollTop)}`)
- const isEffectivelyRendered = (element: Element): boolean => {
- const rect = element.getBoundingClientRect()
- const view = element.ownerDocument.defaultView
- if (
- !view ||
- rect.width <= 1 ||
- rect.height <= 1 ||
- rect.right <= 0 ||
- rect.bottom <= 0 ||
- rect.left >= view.innerWidth ||
- rect.top >= view.innerHeight
- ) {
- return false
- }
- for (let current: Element | null = element; current; ) {
- const currentView: Window | null = current.ownerDocument.defaultView
- const style = currentView?.getComputedStyle(current)
- const opacity = Number.parseFloat(style?.opacity || '1')
- if (
- !style ||
- style.display === 'none' ||
- style.visibility === 'hidden' ||
- style.contentVisibility === 'hidden' ||
- (Number.isFinite(opacity) && opacity <= 0.01) ||
- current.hasAttribute('hidden') ||
- current.getAttribute('aria-hidden') === 'true'
- ) {
- return false
- }
- if (current.parentElement) current = current.parentElement
- else {
- const root = current.getRootNode()
- current = 'host' in root ? (root.host as Element) : null
- }
- }
- return true
- }
-
- const targetState =
- typeof elementId !== 'number'
- ? undefined
- : observedElement
- ? {
- present: true,
- rendered: isEffectivelyRendered(observedElement),
- ariaExpanded: observedElement.getAttribute('aria-expanded'),
- ariaSelected: observedElement.getAttribute('aria-selected'),
- ariaPressed: observedElement.getAttribute('aria-pressed'),
- ariaChecked: observedElement.getAttribute('aria-checked'),
- checked:
- 'checked' in observedElement
- ? Boolean((observedElement as HTMLInputElement).checked)
- : undefined,
- selected:
- 'selected' in observedElement
- ? Boolean((observedElement as HTMLOptionElement).selected)
- : undefined,
- open: observedElement.hasAttribute('open'),
- hidden:
- observedElement.hasAttribute('hidden') ||
- observedElement.getAttribute('aria-hidden') === 'true',
- }
- : { present: false, rendered: false }
-
return {
url: observedWindow.location.href.slice(0, 4096),
title: observedDocument.title.slice(0, 500),
@@ -2591,6 +2672,102 @@ export function readSelectElementState(id: number): unknown {
}
}
+export function readCheckableElementState(id: number): unknown {
+ const resolver = window.__simAgentResolveElement
+ const resolved = resolver?.(id)
+ const registered = resolver ? resolved?.element : (window.__simAgentElements || [])[id]
+ const candidate =
+ String(registered?.tagName || '').toUpperCase() === 'LABEL'
+ ? (registered as HTMLLabelElement).control
+ : registered
+ if (!candidate || !candidate.isConnected) {
+ return { error: 'stale', reason: window.__simAgentStaleReason }
+ }
+
+ const tag = String(candidate.tagName || '').toUpperCase()
+ const type =
+ tag === 'INPUT' ? String((candidate as HTMLInputElement).type || '').toLowerCase() : ''
+ const role = String(candidate.getAttribute('role') || '').toLowerCase()
+ const isNative = tag === 'INPUT' && (type === 'checkbox' || type === 'radio')
+ const isAria = ['checkbox', 'radio', 'switch', 'menuitemcheckbox', 'menuitemradio'].includes(role)
+ if (!isNative && !isAria) return { error: 'not-checkable' }
+
+ const ariaChecked = candidate.getAttribute('aria-checked')
+ const checked = isNative
+ ? (candidate as HTMLInputElement).indeterminate
+ ? 'mixed'
+ : Boolean((candidate as HTMLInputElement).checked)
+ : ariaChecked === 'true'
+ ? true
+ : ariaChecked === 'false'
+ ? false
+ : ariaChecked
+ let disabled = candidate.matches(':disabled')
+ for (let ancestor: Element | null = candidate; ancestor && !disabled; ) {
+ disabled = ancestor.getAttribute('aria-disabled') === 'true'
+ const root = ancestor.getRootNode()
+ ancestor = ancestor.parentElement ?? ('host' in root ? (root.host as Element) : null)
+ }
+ return {
+ checked,
+ disabled,
+ readOnly:
+ (candidate as Element & { readOnly?: boolean }).readOnly === true ||
+ candidate.getAttribute('aria-readonly') === 'true',
+ kind: isNative ? `input:${type}` : `role:${role}`,
+ refRecovered: resolved?.recovered === true,
+ }
+}
+
+export function getElementScreenshotRect(id: number): unknown {
+ const resolver = window.__simAgentResolveElement
+ const resolved = resolver?.(id, false)
+ const element = resolver ? resolved?.element : (window.__simAgentElements || [])[id]
+ if (!element || !element.isConnected) {
+ return { error: 'stale', reason: window.__simAgentStaleReason }
+ }
+
+ if (element.ownerDocument !== document) return { error: 'framed-screenshot' }
+ const rect = element.getBoundingClientRect()
+ const view = element.ownerDocument.defaultView
+ if (!view) return { error: 'stale', reason: window.__simAgentStaleReason }
+ for (let current: Element | null = element; current; ) {
+ const currentView: Window | null = current.ownerDocument.defaultView
+ const style = currentView?.getComputedStyle(current)
+ const opacity = Number.parseFloat(style?.opacity || '1')
+ if (
+ !style ||
+ style.display === 'none' ||
+ style.visibility === 'hidden' ||
+ style.contentVisibility === 'hidden' ||
+ (Number.isFinite(opacity) && opacity <= 0.01) ||
+ current.hasAttribute('hidden') ||
+ current.getAttribute('aria-hidden') === 'true'
+ ) {
+ return { error: 'not-visible' }
+ }
+ if (current.parentElement) current = current.parentElement
+ else {
+ const root = current.getRootNode()
+ current = 'host' in root ? (root.host as Element) : null
+ }
+ }
+
+ const left = Math.max(0, rect.left)
+ const top = Math.max(0, rect.top)
+ const right = Math.min(view.innerWidth, rect.right)
+ const bottom = Math.min(view.innerHeight, rect.bottom)
+ if (right - left <= 1 || bottom - top <= 1) return { error: 'not-visible' }
+ return {
+ x: left,
+ y: top,
+ width: right - left,
+ height: bottom - top,
+ element: element.tagName.toLowerCase().slice(0, 80),
+ refRecovered: resolved?.recovered === true,
+ }
+}
+
export function hoverElement(id: number): unknown {
const resolver = window.__simAgentResolveElement
const resolved = resolver?.(id)
diff --git a/apps/desktop/src/main/browser-agent/session.test.ts b/apps/desktop/src/main/browser-agent/session.test.ts
index 2f81cc51f8d..b7f62e74266 100644
--- a/apps/desktop/src/main/browser-agent/session.test.ts
+++ b/apps/desktop/src/main/browser-agent/session.test.ts
@@ -4661,12 +4661,14 @@ describe('reopening a closed tab', () => {
})
describe('importAgentCookies', () => {
- /** Points the mocked partition at a cookie jar and returns its `set` spy. */
- function withCookieJar(set: ReturnType): SessionModule {
+ function withCookieJar(
+ set: ReturnType,
+ flushStore = vi.fn(async () => {})
+ ): SessionModule {
// The partition is resolved per call, not captured at module load, so
// re-mocking it here is enough — no module reload required.
vi.mocked(electronSession.fromPartition).mockReturnValue({
- cookies: { set },
+ cookies: { set, flushStore },
} as unknown as ReturnType)
return sessionModule
}
@@ -4683,7 +4685,8 @@ describe('importAgentCookies', () => {
it('writes every cookie into the dedicated browser profile', async () => {
const set = vi.fn(async () => {})
- const session = withCookieJar(set)
+ const flushStore = vi.fn(async () => {})
+ const session = withCookieJar(set, flushStore)
const result = await session.importAgentCookies([cookie('a'), cookie('b')])
@@ -4691,6 +4694,8 @@ describe('importAgentCookies', () => {
expect(electronSession.fromPartition).toHaveBeenCalledWith('persist:sim-browser-agent')
expect(set).toHaveBeenCalledTimes(2)
expect(set).toHaveBeenNthCalledWith(1, cookie('a'))
+ expect(flushStore).toHaveBeenCalledOnce()
+ expect(flushStore.mock.invocationCallOrder[0]).toBeGreaterThan(set.mock.invocationCallOrder[1])
})
it('counts a rejected cookie without losing the rest', async () => {
@@ -4709,9 +4714,22 @@ describe('importAgentCookies', () => {
it('does nothing when there is nothing to import', async () => {
const set = vi.fn(async () => {})
- const session = withCookieJar(set)
+ const flushStore = vi.fn(async () => {})
+ const session = withCookieJar(set, flushStore)
await expect(session.importAgentCookies([])).resolves.toEqual({ imported: 0, failed: 0 })
expect(set).not.toHaveBeenCalled()
+ expect(flushStore).not.toHaveBeenCalled()
+ })
+
+ it('does not report a durable import when flushing to disk fails', async () => {
+ const session = withCookieJar(
+ vi.fn(async () => {}),
+ vi.fn(async () => {
+ throw new Error('Disk unavailable')
+ })
+ )
+
+ await expect(session.importAgentCookies([cookie('a')])).rejects.toThrow('Disk unavailable')
})
})
diff --git a/apps/desktop/src/main/browser-agent/session.ts b/apps/desktop/src/main/browser-agent/session.ts
index 9bedd83f938..2d3807af204 100644
--- a/apps/desktop/src/main/browser-agent/session.ts
+++ b/apps/desktop/src/main/browser-agent/session.ts
@@ -1196,6 +1196,7 @@ export async function importAgentCookies(
failed += 1
}
}
+ if (imported > 0) await jar.flushStore()
return { imported, failed }
}
diff --git a/apps/desktop/src/main/browser-credentials/vault.test.ts b/apps/desktop/src/main/browser-credentials/vault.test.ts
index 3fc1d958544..0a22efa919a 100644
--- a/apps/desktop/src/main/browser-credentials/vault.test.ts
+++ b/apps/desktop/src/main/browser-credentials/vault.test.ts
@@ -39,6 +39,25 @@ const CANDIDATES = [
]
describe('CredentialVault', () => {
+ it('retains imported logins when the vault is reopened and does not duplicate a re-import', async () => {
+ const original = new CredentialVault(vaultPath, encryption())
+ await original.importCredentials(CANDIDATES, 'replace')
+ const metadata = await original.list()
+
+ const reopened = new CredentialVault(vaultPath, encryption())
+ expect(await reopened.list()).toEqual(metadata)
+ expect(await reopened.readForFill(metadata[0].id, metadata[0].origin)).toEqual({
+ username: 'ada',
+ password: 'hunter2',
+ })
+ expect(await reopened.importCredentials(CANDIDATES, 'replace')).toEqual({
+ added: 0,
+ updated: 0,
+ skipped: 2,
+ })
+ expect(await reopened.list()).toEqual(metadata)
+ })
+
it('stores and lists credentials without their passwords', async () => {
const vault = new CredentialVault(vaultPath, encryption())
diff --git a/apps/desktop/src/main/browser-import/import-service.test.ts b/apps/desktop/src/main/browser-import/import-service.test.ts
index f1c9567c1a7..75d04ce9722 100644
--- a/apps/desktop/src/main/browser-import/import-service.test.ts
+++ b/apps/desktop/src/main/browser-import/import-service.test.ts
@@ -616,26 +616,33 @@ describe('importChromePasswords', () => {
expect(importCredentials).toHaveBeenCalledWith([expect.any(Object)], 'replace')
})
- it('keeps credentials from one password store when the other is unreadable', async () => {
- const localPath = '/arc/Default/Login Data'
- const accountPath = '/arc/Default/Login Data For Account'
- const deps = createDeps({
- listProfiles: async () => [
- { ...PROFILES[1], id: 'arc:Default', loginDataPaths: [localPath, accountPath] },
- ],
- readPasswords: async (path) => {
- if (path === accountPath) {
- throw new ImportFailure('unsupported-schema', 'unknown account-store schema')
- }
- return readPasswords()
- },
- })
+ it.each(['local', 'account'])(
+ 'reports a partial import when the %s password store is unreadable',
+ async (failedStore) => {
+ const localPath = '/arc/Default/Login Data'
+ const accountPath = '/arc/Default/Login Data For Account'
+ const deps = createDeps({
+ listProfiles: async () => [
+ { ...PROFILES[1], id: 'arc:Default', loginDataPaths: [localPath, accountPath] },
+ ],
+ readPasswords: async (path) => {
+ if (path === (failedStore === 'local' ? localPath : accountPath)) {
+ throw new ImportFailure('unsupported-schema', 'unknown account-store schema')
+ }
+ return readPasswords()
+ },
+ })
- await expect(importChromePasswords('arc:Default', 'replace', deps)).resolves.toMatchObject({
- passwordsAdded: 1,
- passwordsSkipped: 0,
- })
- })
+ await expect(importChromeData('arc:Default', 'replace', deps)).resolves.toMatchObject({
+ cookies: { cookiesImported: 1 },
+ passwords: {
+ passwordsAdded: 1,
+ passwordsSkipped: 0,
+ error: 'unsupported-schema',
+ },
+ })
+ }
+ )
it('surfaces a failed password store when the other store only has unreadable rows', async () => {
const localPath = '/arc/Default/Login Data'
diff --git a/apps/desktop/src/main/browser-import/import-service.ts b/apps/desktop/src/main/browser-import/import-service.ts
index 11c943efa12..8c704361d16 100644
--- a/apps/desktop/src/main/browser-import/import-service.ts
+++ b/apps/desktop/src/main/browser-import/import-service.ts
@@ -302,6 +302,7 @@ async function runPasswordImport(
passwordsAdded: outcome.added,
passwordsUpdated: outcome.updated,
passwordsSkipped: outcome.skipped + read.skipped,
+ ...(read.error ? { error: read.error } : {}),
}
logger.info('Chrome password import finished', {
added: result.passwordsAdded,
@@ -321,6 +322,7 @@ async function runPasswordImport(
* source order breaks ties deterministically before applying the vault policy.
*
* One damaged store does not discard credentials already read from the other.
+ * A partial read carries its failure to the UI alongside the imported counts.
* If no store can produce any useful signal, the first concrete reader error
* is surfaced instead of reporting a misleading successful import of zero.
*/
@@ -328,7 +330,7 @@ async function readProfilePasswords(
paths: readonly string[],
key: Buffer,
deps: ImportServiceDeps
-): Promise {
+): Promise {
const combined: ReadPasswordsResult = { credentials: [], skipped: 0, rowsSeen: 0 }
const credentialIndexes = new Map()
let successfulReads = 0
@@ -379,6 +381,7 @@ async function readProfilePasswords(
if (firstFailure !== undefined) {
// Category only: database names and paths are deliberately absent.
logger.warn('Could not read every password store in the selected browser profile')
+ return { ...combined, error: categorize(firstFailure, 'password') }
}
return combined
}
diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx
index 23ddfd3d0ce..867a81af5c2 100644
--- a/apps/docs/components/icons.tsx
+++ b/apps/docs/components/icons.tsx
@@ -2710,6 +2710,25 @@ export function BrexIcon(props: SVGProps) {
)
}
+/**
+ * Official QuickBooks circular mark, cropped from the user-supplied
+ * Intuit_QuickBooks_logo.svg wordmark.
+ */
+export function QuickBooksIcon(props: SVGProps) {
+ return (
+
+
+
+
+ )
+}
+
export function BrightDataIcon(props: SVGProps) {
return (
) {
)
}
+export function SSMIcon(props: SVGProps) {
+ return (
+
+
+
+
+
+ )
+}
+
export function SQSIcon(props: SVGProps) {
return (
) {
)
}
+export function CloudTrailIcon(props: SVGProps) {
+ return (
+
+
+
+
+
+ )
+}
+
export function CloudWatchIcon(props: SVGProps) {
return (
= {
clickup: ClickUpIcon,
cloudflare: CloudflareIcon,
cloudformation: CloudFormationIcon,
+ cloudtrail: CloudTrailIcon,
cloudwatch: CloudWatchIcon,
codepipeline: CodePipelineIcon,
confluence: ConfluenceIcon,
@@ -500,6 +504,7 @@ export const blockTypeToIconMap: Record = {
pulse_v2: PulseIcon,
qdrant: QdrantIcon,
quartr: QuartrIcon,
+ quickbooks: QuickBooksIcon,
quiver: QuiverIcon,
rabbitmq: RabbitmqIcon,
railway: RailwayIcon,
@@ -546,6 +551,7 @@ export const blockTypeToIconMap: Record = {
sqs: SQSIcon,
square: SquareIcon,
ssh: SshIcon,
+ ssm: SSMIcon,
stagehand: StagehandIcon,
stripe: StripeIcon,
sts: STSIcon,
diff --git a/apps/docs/content/docs/desktop/index.mdx b/apps/docs/content/docs/desktop/index.mdx
new file mode 100644
index 00000000000..05c219bb2e8
--- /dev/null
+++ b/apps/docs/content/docs/desktop/index.mdx
@@ -0,0 +1,155 @@
+---
+title: Sim Desktop
+description: Install the macOS app — Sim in its own window, with a built-in browser, a terminal, and access to local folders.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout'
+import { Step, Steps } from 'fumadocs-ui/components/steps'
+import { Image } from '@/components/ui/image'
+
+Sim Desktop is the macOS app for your Sim workspace. Everything the web app does, it does — plus the things a browser tab cannot: a built-in browser and terminal, read-only access to folders you pick, and native notifications when a task finishes.
+
+
+
+## Download
+
+**[Download Sim Desktop for macOS](https://sim.ai/api/desktop/update/download)**
+
+One universal build runs natively on both Apple Silicon and Intel Macs. It is signed and notarized by Sim, so Gatekeeper accepts it with no override.
+
+
+ That link is never version-pinned — it is a redirect resolved at request time, so it always lands on the newest release. Bookmark it, share it, or put it in an MDM policy; it stays correct.
+
+ It resolves against sim.ai. Every deployment serves the same endpoint on its own origin, so if you self-host use yours — `https://sim.example.com/api/desktop/update/download` — to get the build your deployment offers.
+
+
+To install a specific version instead of the newest one, pick it from [the releases page](https://github.com/simstudioai/sim/releases) and download that release's `Sim--universal.dmg`.
+
+## Install
+
+
+
+
+
+### Open the disk image and drag Sim to Applications
+
+
+ Install to `/Applications`. macOS App Translocation runs an app from a randomized read-only path when it is launched from Downloads, which silently breaks auto-updates.
+
+
+
+
+
+
+### Sign in
+
+Launch Sim and sign in as you normally would.
+
+Google, Microsoft, and SSO sign-ins finish in your default browser — those providers refuse to render inside an embedded browser. Sim opens the page, you approve, and the browser hands the session back to the app. The app gets its own session, so signing out of one surface does not sign out the other.
+
+
+
+
+
+### Point it at your deployment, if you self-host
+
+Fresh installs open sim.ai. To use your own deployment, choose **Sim → Server…** in the menu bar and enter its URL. See [Desktop App on Your Deployment](/platform/self-hosting/desktop) for what changes when you switch.
+
+
+
+
+
+## What the desktop app adds
+
+- **A built-in browser.** A real browser inside the app, with its own tabs, saved passwords, and sessions. Chat can drive it — sign in once and your agents work on the sites you are already signed into.
+- **A built-in terminal.** Real shell sessions in a panel next to Chat, with tmux and shell integration, that Chat can run commands in.
+- **Local folder access.** When a task needs a folder on your Mac, Chat offers to open the native folder picker. The grant is read-only, scoped to the folder you picked, and revocable.
+- **Notifications.** A native notification when a Chat task finishes. Clicking it opens that chat. Tasks that end in an error, or that have another message queued behind them, do not notify.
+- **Control Center.** A menu-bar icon with your recent chats, so Sim is one click away from any app.
+- **Launch at login.** Sim starts with your Mac, and however you launch it, it opens where you left off.
+
+Both the browser and the terminal are capabilities you grant, not defaults you are stuck with — each has a single switch in settings that turns it off entirely.
+
+
+ **What a folder grant does and does not do.** Granting a folder does not copy or upload it, and an agent cannot attach or stage a file from it — that stays your deliberate act. But when an agent reads or searches inside the grant, what it reads is a tool result, and tool results go to your Sim server and to the model like anything else in the conversation. The file stays on your Mac; what an agent reads out of it does not.
+
+
+## Keyboard shortcuts
+
+These are the app's own shortcuts. The [workflow editor and table shortcuts](/keyboard-shortcuts) work the same in the app as in the browser.
+
+| Shortcut | Action |
+|---|---|
+| `Cmd` + `K` | Search |
+| `Cmd` + `B` | Toggle the sidebar |
+| `Cmd` + `N` | New chat |
+| `Cmd` + `Shift` + `N` | New window |
+| `Cmd` + `,` | Settings |
+| `Cmd` + `[` | Back |
+| `Cmd` + `R` | Reload |
+| `Cmd` + `0` / `+` / `-` | Reset, increase, or decrease zoom |
+
+With the built-in browser or terminal focused, the tab shortcuts act on its tabs rather than on the window:
+
+| Shortcut | Action |
+|---|---|
+| `Cmd` + `T` | New tab |
+| `Cmd` + `W` | Close tab |
+| `Cmd` + `Shift` + `T` | Reopen the last closed tab |
+| `Ctrl` + `Tab` / `Ctrl` + `Shift` + `Tab` | Next / previous tab |
+| `Cmd` + `1`–`8` | Jump to that tab |
+| `Cmd` + `9` | Jump to the last tab |
+| `Cmd` + `L` | Focus the address bar (browser only) |
+| `Cmd` + `F` | Find on the page (browser only) |
+
+## Settings
+
+The desktop app adds three sections under **Settings → Account**. They appear only when you are running the app, and they apply to this Mac rather than to your account.
+
+### Desktop
+
+- **Launch Sim at login**
+- **Show Sim in Control Center** — the menu-bar icon
+- **Automatically download updates**
+- **Enable desktop notifications**, with **Play notification sounds** and **Notify only when Sim isn't focused**
+
+It also shows the installed version, and the version waiting to be applied when an update is ready.
+
+### Browser
+
+- **Let Chat browse the web** — the master switch for the built-in browser
+- **Search suggestions**, **Theme**, **Default zoom**, and **Download location**
+- **Browsing data** — clear cookies, site data, and cached images and files
+
+### Terminal
+
+- **Let Chat run commands** — the master switch for the built-in terminal
+- **Theme** and **Default zoom**
+
+The menu bar carries the rest: **Sim → Settings…** (`Cmd` + `,`), **Server…** to change deployments, **Check for Updates…**, and **Sign Out**.
+
+## Updates
+
+Sim checks the deployment it is pointed at rather than a global feed. How it applies what it finds depends on how the app was installed. Nothing is ever forced mid-session either way.
+
+**Installed in `/Applications`, signed by Sim** — what the download link above gives you. The app replaces itself. With **Automatically download updates** on, it downloads in the background and offers to restart; choose **Later** and the update applies the next time you quit. With it off, nothing downloads until you ask: **Sim → Check for Updates…** reports the available version and waits for you to choose **Download**.
+
+**Anywhere else** — outside `/Applications`, or a build not signed with a Developer ID. The app cannot replace itself, so it offers you the installer to download and swap in by hand.
+
+Updates come from the deployment you are connected to, so a self-hosted install controls which build its own users are offered. That control depends on the feed staying reachable: if it is not, a self-updating stable build falls back to Sim's public GitHub releases rather than stalling. See [Desktop App on Your Deployment](/platform/self-hosting/desktop).
+
+## Requirements
+
+- **macOS 12 Monterey or later**, on Apple Silicon or Intel.
+- **Outbound access to your Sim deployment**, and to `github.com`, which is where installers and updates are downloaded from.
+- **If you self-host**, your Sim server needs its own outbound access to both `api.github.com`, which is what resolves *which* release to offer, and `github.com`. An allowlist carrying only `github.com` leaves the download endpoint answering `502`.
+- **A system-trusted TLS certificate**, if you self-host. The app rejects certificate errors outright and offers no override, so a private CA must be installed in the macOS keychain.
+
+
+ The desktop app is macOS-only today. The web app works in any browser on any platform, and your account, workspaces, and workflows are the same either way — the app adds native capabilities on top, it does not hold anything of its own.
+
diff --git a/apps/docs/content/docs/integrations/cloudtrail.mdx b/apps/docs/content/docs/integrations/cloudtrail.mdx
new file mode 100644
index 00000000000..bffd59d6b61
--- /dev/null
+++ b/apps/docs/content/docs/integrations/cloudtrail.mdx
@@ -0,0 +1,480 @@
+---
+title: CloudTrail
+description: Audit who did what in AWS with CloudTrail event history and Lake queries
+---
+
+import { BlockInfoCard } from "@/components/ui/block-info-card"
+
+
+
+{/* MANUAL-CONTENT-START:intro */}
+[AWS CloudTrail](https://aws.amazon.com/cloudtrail/) records who did what in your AWS accounts. An API call — from the console, the CLI, an SDK, or another AWS service — is captured as an event with the calling identity, source IP, parameters, and result. It is the system of record for security investigation, compliance evidence, and answering "what changed?"
+
+What lands in that record is set by configuration, not assumed. Trails and event data stores log management events by default; data events, network activity events, and Insights events are captured only where you configure selectors for them. Read a trail's selectors before you treat its history as complete.
+
+With AWS CloudTrail, you can:
+
+- **Look up recent activity**: Search the last 90 days of management events by user, event name, resource, or event source
+- **Inspect trail configuration**: Describe trails, check logging status, and read the event and Insights selectors that decide what gets captured
+- **Query history with SQL**: Run CloudTrail Lake queries across event data stores for analysis that reaches further back than event lookup
+- **Confirm coverage**: Verify that logging is actually enabled and that multi-region and organization trails are delivering
+
+In Sim, CloudTrail is the audit half of the AWS story. Where IAM and Identity Center answer *who has access*, CloudTrail answers *what they actually did with it* — so an agent can take a suspicious permission change and trace it back to the principal, the source IP, and the moment it happened, then hand a written timeline to whoever needs to act on it.
+
+This block is read-only with one narrow exception: `Cancel Query` stops a running CloudTrail Lake query. It never enables or disables logging, alters trail configuration, or deletes a trail. Every operation it ships is covered by this policy, with no residual write risk:
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "cloudtrail:DescribeTrails",
+ "cloudtrail:GetTrail",
+ "cloudtrail:GetTrailStatus",
+ "cloudtrail:GetEventSelectors",
+ "cloudtrail:GetInsightSelectors",
+ "cloudtrail:GetEventDataStore",
+ "cloudtrail:ListTrails",
+ "cloudtrail:ListEventDataStores",
+ "cloudtrail:ListTags",
+ "cloudtrail:LookupEvents",
+ "cloudtrail:StartQuery",
+ "cloudtrail:DescribeQuery",
+ "cloudtrail:GetQueryResults",
+ "cloudtrail:CancelQuery"
+ ],
+ "Resource": "*"
+ }
+ ]
+}
+```
+
+`cloudtrail:CancelQuery` is the action `Cancel Query` needs, and it is not implied by `Describe*`, `Get*`, or `List*` — omit it and that one operation fails with an access-denied error. Note that `Start Query` is billed per GB scanned and consumes your account's concurrent-query quota of 10.
+
+`Lookup Events` is limited by AWS to two requests per second per account per Region. Each call uses AWS adaptive retry mode and allows up to six attempts, so a throttled request backs off exponentially with jitter and usually succeeds instead of surfacing an error. That is a retry budget, not a guarantee: sustained throttling past six attempts fails the call with a `ThrottlingException`, and because a fresh SDK client is built per invocation, adaptive mode's client-side rate limiter carries no pacing state between calls. `Lookup Events` also returns one page per call — feed `nextToken` back in to walk a broad search, and expect to handle a throttling error on a long paging loop.
+{/* MANUAL-CONTENT-END */}
+
+
+## Usage Instructions
+
+Integrate AWS CloudTrail into workflows. Look up the last 90 days of management and Insights events by user, event name, resource, or access key; inspect trail configuration, logging status, and event selectors; and run SQL queries against CloudTrail Lake event data stores. This block never changes trail or event data store configuration, and never starts or stops logging. Starting and cancelling a Lake query are the only actions that are not reads, and AWS bills Lake queries on the data they scan. Requires AWS access key and secret access key.
+
+
+
+## Actions
+
+### CloudTrail Look Up Events
+
+Look up AWS CloudTrail management or Insights events from the last 90 days in a Region
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `attributeKey` | string | No | Lookup attribute to filter on: AccessKeyId, EventId, EventName, EventSource, ReadOnly, ResourceName, ResourceType, or Username. Must be paired with attributeValue |
+| `attributeValue` | string | No | Value the lookup attribute must equal. Must be paired with attributeKey |
+| `startTime` | string | No | Only return events at or after this ISO 8601 timestamp |
+| `endTime` | string | No | Only return events at or before this ISO 8601 timestamp |
+| `eventCategory` | string | No | Set to the value insight to return CloudTrail Insights events instead of management events |
+| `maxResults` | number | No | Number of events to return, 1 to 50 \(default 50\) |
+| `nextToken` | string | No | Pagination token from a previous lookup, which must repeat the same filters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `events` | array | Matching events, most recent first |
+| ↳ `eventId` | string | CloudTrail event ID |
+| ↳ `eventName` | string | API action that was called |
+| ↳ `readOnly` | string | Whether the action was read-only, as the string 'true' or 'false' |
+| ↳ `accessKeyId` | string | Access key ID used to make the call, when applicable |
+| ↳ `eventTime` | string | When the event occurred \(ISO 8601\) |
+| ↳ `eventSource` | string | AWS service endpoint that recorded the event |
+| ↳ `username` | string | Name of the principal that made the call |
+| ↳ `resources` | array | Resources referenced by the event, as resourceType and resourceName |
+| ↳ `cloudTrailEvent` | object | Full CloudTrail event record parsed from JSON, including userIdentity, sourceIPAddress, userAgent, requestParameters, responseElements, and errorCode |
+| ↳ `cloudTrailEventRaw` | string | Raw CloudTrail event JSON string, populated only when it could not be parsed |
+| `nextToken` | string | Pagination token for the next page of events |
+
+### CloudTrail Describe Trails
+
+Retrieve the full configuration of one or more CloudTrail trails in the current Region
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `trailNameList` | string | No | Comma-separated trail names or ARNs. Leave empty to describe every trail in the Region. Trails in another Region must be given as ARNs |
+| `includeShadowTrails` | boolean | No | Include shadow trails \(replications of trails created in another Region, and organization trails in member accounts\). Defaults to true |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `trails` | array | Full configuration of each matching trail |
+| ↳ `name` | string | Trail name |
+| ↳ `s3BucketName` | string | S3 bucket that receives log files |
+| ↳ `s3KeyPrefix` | string | S3 key prefix for delivered log files |
+| ↳ `snsTopicName` | string | SNS topic notified on log delivery |
+| ↳ `snsTopicArn` | string | ARN of that SNS topic |
+| ↳ `includeGlobalServiceEvents` | boolean | Whether global service events are recorded |
+| ↳ `isMultiRegionTrail` | boolean | Whether the trail records events in all Regions |
+| ↳ `homeRegion` | string | Region in which the trail was created |
+| ↳ `trailArn` | string | ARN of the trail |
+| ↳ `logFileValidationEnabled` | boolean | Whether log file integrity validation is enabled |
+| ↳ `cloudWatchLogsLogGroupArn` | string | CloudWatch Logs log group receiving events |
+| ↳ `cloudWatchLogsRoleArn` | string | Role CloudTrail assumes to write to CloudWatch Logs |
+| ↳ `kmsKeyId` | string | KMS key used to encrypt log files |
+| ↳ `hasCustomEventSelectors` | boolean | Whether the trail has custom event selectors |
+| ↳ `hasInsightSelectors` | boolean | Whether the trail has Insights event selectors |
+| ↳ `isOrganizationTrail` | boolean | Whether the trail is an organization trail |
+
+### CloudTrail Get Trail
+
+Retrieve the settings of a single CloudTrail trail by name or ARN
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Trail name, or the trail ARN for a trail in another Region |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `name` | string | Trail name |
+| `s3BucketName` | string | Name of the S3 bucket that receives log files |
+| `s3KeyPrefix` | string | S3 key prefix prepended to delivered log files |
+| `snsTopicName` | string | Name of the SNS topic notified on log delivery |
+| `snsTopicArn` | string | ARN of the SNS topic notified on log delivery |
+| `includeGlobalServiceEvents` | boolean | Whether the trail records global service events |
+| `isMultiRegionTrail` | boolean | Whether the trail records events in all Regions |
+| `homeRegion` | string | Region in which the trail was created |
+| `trailArn` | string | ARN of the trail |
+| `logFileValidationEnabled` | boolean | Whether log file integrity validation is enabled |
+| `cloudWatchLogsLogGroupArn` | string | ARN of the CloudWatch Logs log group receiving events |
+| `cloudWatchLogsRoleArn` | string | ARN of the role CloudTrail assumes to write to CloudWatch Logs |
+| `kmsKeyId` | string | KMS key used to encrypt log files |
+| `hasCustomEventSelectors` | boolean | Whether the trail has custom event selectors |
+| `hasInsightSelectors` | boolean | Whether the trail has Insights event selectors |
+| `isOrganizationTrail` | boolean | Whether the trail is an organization trail |
+
+### CloudTrail Get Trail Status
+
+Check whether a CloudTrail trail is logging and surface its most recent delivery errors
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Trail name, or the trail ARN. An organization trail read from a member account must be given as an ARN |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `isLogging` | boolean | Whether the trail is currently recording API calls |
+| `latestDeliveryError` | string | Most recent S3 error encountered delivering log files |
+| `latestDeliveryTime` | string | When log files were last delivered to S3 \(ISO 8601\) |
+| `latestNotificationError` | string | Most recent SNS error encountered sending a notification |
+| `latestNotificationTime` | string | When the last SNS notification was sent \(ISO 8601\) |
+| `latestCloudWatchLogsDeliveryError` | string | Most recent CloudWatch Logs delivery error |
+| `latestCloudWatchLogsDeliveryTime` | string | When events were last delivered to CloudWatch Logs \(ISO 8601\) |
+| `latestDigestDeliveryError` | string | Most recent S3 error encountered delivering a digest file |
+| `latestDigestDeliveryTime` | string | When a digest file was last delivered to S3 \(ISO 8601\) |
+| `startLoggingTime` | string | When logging was most recently started \(ISO 8601\) |
+| `stopLoggingTime` | string | When logging was most recently stopped \(ISO 8601\) |
+
+### CloudTrail List Trails
+
+List the ARN, name, and home Region of every CloudTrail trail visible to the account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `nextToken` | string | No | Pagination token from a previous list request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `trails` | array | Trail summaries |
+| ↳ `trailArn` | string | ARN of the trail |
+| ↳ `name` | string | Trail name |
+| ↳ `homeRegion` | string | Region in which the trail was created |
+| `nextToken` | string | Pagination token for the next page of trails, or null on the last page |
+
+### CloudTrail Get Event Selectors
+
+Read which management, data, and network activity events a CloudTrail trail is configured to log
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `trailName` | string | Yes | Trail name or trail ARN |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `trailArn` | string | ARN of the trail that owns these selectors |
+| `eventSelectors` | array | Basic event selectors configured on the trail |
+| ↳ `readWriteType` | string | All, ReadOnly, or WriteOnly |
+| ↳ `includeManagementEvents` | boolean | Whether management events are recorded |
+| ↳ `dataResources` | array | Data resources logged by the selector, as type and values |
+| ↳ `excludeManagementEventSources` | array | Event sources excluded from management event logging |
+| `advancedEventSelectors` | array | Advanced event selectors configured on the trail |
+| ↳ `name` | string | Name of the advanced event selector |
+| ↳ `fieldSelectors` | array | Field selectors, each with field plus its equals, startsWith, endsWith, notEquals, notStartsWith, and notEndsWith values |
+
+### CloudTrail Get Insight Selectors
+
+Read which CloudTrail Insights types are enabled on a trail or event data store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `trailName` | string | No | Trail name or trail ARN. Cannot be combined with eventDataStore |
+| `eventDataStore` | string | No | Event data store ARN, or the ID suffix of that ARN. Cannot be combined with trailName |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `trailArn` | string | ARN of the trail whose Insights selectors were read |
+| `eventDataStoreArn` | string | ARN of the source event data store that enabled Insights events |
+| `insightsDestination` | string | ARN of the destination event data store that logs Insights events |
+| `insightSelectors` | array | Enabled Insights types and their event categories |
+| ↳ `insightType` | string | ApiCallRateInsight or ApiErrorRateInsight |
+| ↳ `eventCategories` | array | Event categories the Insights type applies to: Management, Data, or both |
+
+### CloudTrail Start Query
+
+Start a CloudTrail Lake SQL query over an event data store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `queryStatement` | string | No | SQL query to run, up to 10,000 characters. The event data store ID is named in the FROM clause. Supply this or queryAlias, not both |
+| `queryAlias` | string | No | Alias of a query template used by CloudTrail Lake dashboards. Supply this or queryStatement, not both |
+| `queryParameters` | string | No | Comma-separated parameter values for the query alias, up to 10 values |
+| `deliveryS3Uri` | string | No | S3 URI where CloudTrail delivers the query results \(e.g., s3://my-bucket/results\) |
+| `eventDataStoreOwnerAccountId` | string | No | Account ID of the event data store owner, for a shared event data store |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queryId` | string | ID of the started query. Pass it to Describe Query to poll status, or to Get Query Results to page through rows |
+| `eventDataStoreOwnerAccountId` | string | Account ID of the event data store owner |
+
+### CloudTrail Describe Query
+
+Check the status, run time, and scan statistics of a CloudTrail Lake query
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `queryId` | string | No | ID of the query returned by Start Query. Supply this or queryAlias, not both |
+| `queryAlias` | string | No | Query template alias; returns the last run for that alias. Supply this or queryId, not both |
+| `refreshId` | string | No | Dashboard refresh ID, used together with queryAlias |
+| `eventDataStoreOwnerAccountId` | string | No | Account ID of the event data store owner, for a shared event data store |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queryId` | string | ID of the query |
+| `queryString` | string | SQL body of the query |
+| `queryStatus` | string | QUEUED, RUNNING, FINISHED, FAILED, CANCELLED, or TIMED_OUT |
+| `errorMessage` | string | Error message returned if the query failed |
+| `deliveryS3Uri` | string | S3 URI the results were delivered to, if configured |
+| `deliveryStatus` | string | Delivery status of the S3 results \(SUCCESS, FAILED, PENDING, and similar\) |
+| `prompt` | string | Natural-language prompt used to generate the query, if it was generated |
+| `eventDataStoreOwnerAccountId` | string | Account ID of the event data store owner |
+| `eventsMatched` | number | Number of events that matched the query |
+| `eventsScanned` | number | Number of events scanned by the query |
+| `bytesScanned` | number | Bytes scanned by the query |
+| `executionTimeInMillis` | number | Query run time in milliseconds |
+| `creationTime` | string | When the query was created \(ISO 8601\) |
+
+### CloudTrail Get Query Results
+
+Fetch a page of result rows from a finished CloudTrail Lake query
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `queryId` | string | Yes | ID of the query returned by Start Query |
+| `maxQueryResults` | number | No | Maximum rows to return on a single page, 1 to 1000 |
+| `nextToken` | string | No | Pagination token from a previous results request |
+| `eventDataStoreOwnerAccountId` | string | No | Account ID of the event data store owner, for a shared event data store |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queryStatus` | string | QUEUED, RUNNING, FINISHED, FAILED, CANCELLED, or TIMED_OUT |
+| `rows` | array | Result rows, each flattened into a single object keyed by the query column names |
+| `resultsCount` | number | Number of rows on this page |
+| `totalResultsCount` | number | Total number of rows the query produced |
+| `bytesScanned` | number | Bytes scanned by the query |
+| `errorMessage` | string | Error message returned if the query failed |
+| `nextToken` | string | Pagination token for the next page of rows |
+
+### CloudTrail Cancel Query
+
+Cancel a running CloudTrail Lake query
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `queryId` | string | Yes | ID of the query returned by Start Query |
+| `eventDataStoreOwnerAccountId` | string | No | Account ID of the event data store owner, for a shared event data store |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queryId` | string | ID of the cancelled query |
+| `queryStatus` | string | Status AWS reported for the query after the cancellation request. Cancellation is asynchronous, so this is typically RUNNING or CANCELLED — poll Describe Lake Query for the terminal status |
+| `eventDataStoreOwnerAccountId` | string | Account ID of the event data store owner, when the query was cross-account |
+
+### CloudTrail List Event Data Stores
+
+List the CloudTrail Lake event data stores in the account for the current Region
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `maxResults` | number | No | Maximum event data stores to return on a single page, 1 to 1000 |
+| `nextToken` | string | No | Pagination token from a previous list request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventDataStores` | array | Event data stores in the account for the current Region |
+| ↳ `eventDataStoreArn` | string | ARN of the event data store |
+| ↳ `name` | string | Name of the event data store |
+| ↳ `status` | string | CREATED, ENABLED, PENDING_DELETION, or an ingestion state |
+| ↳ `advancedEventSelectors` | array | Advanced event selectors that define what the store ingests |
+| ↳ `multiRegionEnabled` | boolean | Whether the store collects events from all Regions |
+| ↳ `organizationEnabled` | boolean | Whether the store collects events for the organization |
+| ↳ `retentionPeriod` | number | Retention period in days |
+| ↳ `terminationProtectionEnabled` | boolean | Whether termination protection is enabled |
+| ↳ `createdTimestamp` | string | When the store was created \(ISO 8601\) |
+| ↳ `updatedTimestamp` | string | When the store was last updated \(ISO 8601\) |
+| `nextToken` | string | Pagination token for the next page of event data stores |
+
+### CloudTrail Get Event Data Store
+
+Retrieve the configuration of a single CloudTrail Lake event data store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `eventDataStore` | string | Yes | Event data store ARN, or the ID suffix of that ARN |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventDataStoreArn` | string | ARN of the event data store |
+| `name` | string | Name of the event data store |
+| `status` | string | CREATED, ENABLED, PENDING_DELETION, or an ingestion state |
+| `advancedEventSelectors` | array | Advanced event selectors that define what the store ingests |
+| ↳ `name` | string | Name of the advanced event selector |
+| ↳ `fieldSelectors` | array | Field selectors, each with field plus its equals, startsWith, endsWith, notEquals, notStartsWith, and notEndsWith values |
+| `multiRegionEnabled` | boolean | Whether the store collects events from all Regions |
+| `organizationEnabled` | boolean | Whether the store collects events for the organization |
+| `retentionPeriod` | number | Retention period in days |
+| `terminationProtectionEnabled` | boolean | Whether termination protection is enabled |
+| `createdTimestamp` | string | When the store was created \(ISO 8601\) |
+| `updatedTimestamp` | string | When the store was last updated \(ISO 8601\) |
+| `kmsKeyId` | string | KMS key used to encrypt the store |
+| `billingMode` | string | EXTENDABLE_RETENTION_PRICING or FIXED_RETENTION_PRICING |
+| `federationStatus` | string | Lake Formation federation status |
+| `federationRoleArn` | string | ARN of the role used for Lake Formation federation |
+| `partitionKeys` | array | Partition keys of the event data store |
+| ↳ `name` | string | Partition key name |
+| ↳ `type` | string | Partition key data type |
+
+### CloudTrail List Tags
+
+List the tags on CloudTrail trails, event data stores, dashboards, or channels
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `awsRegion` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `awsAccessKeyId` | string | Yes | AWS access key ID |
+| `awsSecretAccessKey` | string | Yes | AWS secret access key |
+| `resourceIdList` | string | Yes | Comma-separated CloudTrail resource ARNs, up to 20 |
+| `nextToken` | string | No | Reserved for future use by AWS |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `resourceTags` | array | Tags for each requested resource |
+| ↳ `resourceId` | string | ARN of the tagged resource |
+| ↳ `tags` | array | Tags on the resource, as key and value |
+| `nextToken` | string | Reserved for future use by AWS |
+
+
diff --git a/apps/docs/content/docs/integrations/embeddings.mdx b/apps/docs/content/docs/integrations/embeddings.mdx
index d20a6c47628..18e123c8566 100644
--- a/apps/docs/content/docs/integrations/embeddings.mdx
+++ b/apps/docs/content/docs/integrations/embeddings.mdx
@@ -13,19 +13,21 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
{/* MANUAL-CONTENT-START:intro */}
An embedding turns a piece of text into a list of numbers that captures its meaning. Two texts that mean similar things get similar numbers, so you can compare meaning directly instead of matching keywords. That is what powers semantic search, grouping related items, and spotting near-duplicates that are worded differently.
-The Embeddings block generates those numbers using OpenAI, Google Gemini, Cohere, or Mistral. Pick a provider, pick one of its models, pass in text, and get a vector back — one vector per input, in the order you supplied them. You can embed a single string or a list of strings in one call.
+The Embeddings block generates those numbers using OpenAI, Google Gemini, Cohere, Mistral, OpenRouter, or a model on your own Ollama server. Pick a provider, pick one of its models, pass in text, and get a vector back — one vector per input, in the order you supplied them. You can embed a single string or a list of strings in one call.
Models differ in what they are good at and what they cost. `text-embedding-3-small` is the cost-efficient general choice, `gemini-embedding-001` gives the highest retrieval quality, `embed-v4.0` handles multilingual content, and `codestral-embed` is tuned for source code. Some models also let you trade vector size against quality, and some accept a task type so the vector is conditioned for how it will be used — the block only offers those controls for the models that actually support them.
Two things worth knowing before you build on it. Vectors are only comparable when they come from the same model at the same size, so changing either means re-embedding everything you intend to compare. And input longer than the model's limit is shortened to fit rather than rejected, with a warning in the run, so chunk long documents yourself when the tail matters.
-Sim's knowledge bases embed separately, at a fixed vector width and from a smaller set of models. This block is for embedding text yourself inside a workflow.
+Ollama is the exception to most of the above. It runs on your own deployment, so it needs no API key and adds no provider charge — Sim's own per-run charge still applies — and the model list is whatever you have pulled onto that server rather than a catalog Sim maintains. The block reads it live, drops the models that report a non-embedding capability, and shows each one's vector width next to its name where Ollama reports one. A server too old to report either will list its chat models too and label none of them, so check the model you pick. The block offers no task-type or dimension control for Ollama: task conditioning has no equivalent there, and while recent Ollama builds do accept a dimension override for Matryoshka models, older ones silently ignore it, so Sim uses each model's own width rather than one that may or may not take effect. Point Sim at the server with `OLLAMA_URL`. Sim Cloud runs no Ollama of its own, so without that variable the list comes back empty rather than dialling a loopback address that cannot answer — set it to a reachable server and Cloud will use it like any other deployment.
+
+Sim's knowledge bases embed separately: a base fixes one model and one vector width when it is created, from a smaller set of models. This block is for embedding text yourself inside a workflow.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
-Turn text into embedding vectors for semantic search, clustering, and similarity. Supports OpenAI, OpenRouter, Google Gemini, Cohere, and Mistral embedding models.
+Turn text into embedding vectors for semantic search, clustering, and similarity. Supports OpenAI, OpenRouter, Google Gemini, Cohere, and Mistral embedding models, plus embedding models on a self-hosted Ollama.
@@ -151,4 +153,25 @@ Generate embeddings from text using Mistral's embedding models
| `dimensions` | number | Dimensionality of each vector |
| `usage` | json | Token usage |
+### Ollama Embeddings
+
+Generate embeddings on a self-hosted Ollama server
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `input` | string | Yes | Text to embed, or an array of texts to embed in one call |
+| `model` | string | Yes | Embedding model pulled on the configured Ollama server |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `embeddings` | json | Generated embeddings |
+| `model` | string | Model used |
+| `provider` | string | Provider used |
+| `dimensions` | number | Dimensionality of each vector |
+| `usage` | json | Token usage |
+
diff --git a/apps/docs/content/docs/integrations/iam.mdx b/apps/docs/content/docs/integrations/iam.mdx
index 03527393ce6..ba14525abd9 100644
--- a/apps/docs/content/docs/integrations/iam.mdx
+++ b/apps/docs/content/docs/integrations/iam.mdx
@@ -19,9 +19,14 @@ With AWS IAM, you can:
- **Create roles**: Define IAM roles with specific permissions that can be assumed by users, services, or applications for temporary access
- **Attach policies**: Assign managed policies to users and roles to define what actions they can perform on which resources
- **Organize with groups**: Create IAM groups to manage permissions for collections of users, simplifying access management at scale
-- **Control access keys**: Generate and manage programmatic access key pairs for API and CLI access to AWS services
+- **Control access keys**: Generate, list, deactivate, and delete programmatic access key pairs for API and CLI access to AWS services
+- **Simulate policies**: Test whether a principal is allowed to perform specific actions against specific resources, before granting or revoking anything
In Sim, the AWS IAM integration allows your workflows to automate identity management tasks such as provisioning new users, assigning roles and permissions, managing group memberships, and rotating access keys. This is particularly useful for onboarding automation, security compliance workflows, access reviews, and incident response — enabling your agents to manage AWS access control programmatically.
+
+Policy simulation deserves a note, because AWS's model is easy to misread. `Simulate Principal Policy` returns one result per action regardless of how many resource ARNs you pass. The top-level decision is the **aggregate** across every resource — most restrictive wins — and the top-level resource name is an ARN *template* for the resource type, not one of your ARNs. Per-resource answers live in `resourceSpecificResults`, and when you supply concrete ARNs, missing context keys are reported there too rather than at the top level. Read `resourceSpecificResults` whenever you simulate against more than one resource: the aggregate alone will tell you a principal is denied when it is in fact allowed on some of them.
+
+The secret half of a new access key is returned once and is hidden from block output display and execution logs. It stays resolvable downstream, so rotation workflows can pass it straight to the system that needs it — but a block you pass it into will log it under that block's own inputs.
{/* MANUAL-CONTENT-END */}
@@ -317,7 +322,7 @@ List managed IAM policies
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
-| `scope` | string | No | Filter by scope: All, AWS \(AWS-managed\), or Local \(customer-managed\) |
+| `scope` | string | No | Filter by scope. Must be exactly one of: All, AWS \(AWS-managed\), Local \(customer-managed\) |
| `onlyAttached` | boolean | No | If true, only return policies attached to an entity |
| `pathPrefix` | string | No | Path prefix to filter policies |
| `maxItems` | number | No | Maximum number of policies to return \(1-1000, default 100\) |
@@ -327,11 +332,41 @@ List managed IAM policies
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `policies` | json | List of policies with policyName, arn, attachmentCount, and dates |
+| `policies` | json | List of policies with policyName, policyId, arn, path, attachmentCount, isAttachable, defaultVersionId, permissionsBoundaryUsageCount, and dates. AWS never returns policy descriptions from ListPolicies — use IAM Get Policy for a description. |
| `isTruncated` | boolean | Whether there are more results available |
| `marker` | string | Pagination marker for the next page of results |
| `count` | number | Number of policies returned |
+### IAM Get Policy
+
+Get details about a managed IAM policy, including its description — the field ListPolicies never returns
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `policyArn` | string | Yes | ARN of the managed policy to retrieve \(e.g., arn:aws:iam::aws:policy/ReadOnlyAccess\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `policyName` | string | The friendly name of the policy |
+| `policyId` | string | The stable unique ID of the policy |
+| `arn` | string | The ARN of the policy |
+| `path` | string | The path to the policy |
+| `attachmentCount` | number | Number of entities the policy is attached to |
+| `isAttachable` | boolean | Whether the policy can be attached |
+| `createDate` | string | Date the policy was created |
+| `updateDate` | string | Date the policy was last updated |
+| `description` | string | The policy description |
+| `defaultVersionId` | string | The identifier of the default policy version |
+| `permissionsBoundaryUsageCount` | number | Number of entities using the policy as a permissions boundary |
+| `tags` | json | Tags attached to the policy \(key, value pairs\) |
+
### IAM Create Access Key
Create a new access key pair for an IAM user
@@ -376,6 +411,51 @@ Delete an access key pair for an IAM user
| --------- | ---- | ----------- |
| `message` | string | Operation status message |
+### IAM List Access Keys
+
+List an IAM user's access key IDs with their status and age — use to find stale keys and to confirm which keys remain after a rotation
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `userName` | string | No | The IAM user whose keys to list \(defaults to the calling user if omitted\) |
+| `maxItems` | number | No | Maximum number of access keys to return \(1-1000\) |
+| `marker` | string | No | Pagination marker from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `accessKeys` | json | Access key metadata: accessKeyId, userName, status \(Active/Inactive\), createDate. The secret access key is never returned by this operation. |
+| `isTruncated` | boolean | Whether there are more results available |
+| `marker` | string | Pagination marker for the next page of results |
+| `count` | number | Number of access keys returned |
+
+### IAM Update Access Key
+
+Activate or deactivate an IAM access key — deactivate an old key and verify nothing breaks before deleting it
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `accessKeyIdToUpdate` | string | Yes | The access key ID whose status to change |
+| `status` | string | Yes | The status to set. Must be exactly one of: Active, Inactive. An Inactive key is rejected by AWS but can be reactivated. |
+| `userName` | string | No | The IAM user that owns the key \(defaults to the calling user if omitted\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
### IAM List Groups
List IAM groups in your AWS account
@@ -503,7 +583,8 @@ Simulate whether a user, role, or group is allowed to perform specific AWS actio
| `secretAccessKey` | string | Yes | AWS secret access key |
| `policySourceArn` | string | Yes | ARN of the user, group, or role to simulate \(e.g., arn:aws:iam::123456789012:user/alice\) |
| `actionNames` | string | Yes | Comma-separated list of AWS actions to simulate \(e.g., s3:GetObject,ec2:DescribeInstances\) |
-| `resourceArns` | string | No | Comma-separated list of resource ARNs to simulate against \(defaults to * if not provided\) |
+| `resourceArns` | string | No | Comma-separated list of resource ARNs to simulate against \(defaults to * if not provided\). Read the per-ARN verdict from resourceSpecificResults, not from evalDecision. |
+| `contextEntries` | array | No | Condition context keys to supply to the simulation. Without these, any policy gated by a Condition simulates as denied with missing context values. |
| `maxResults` | number | No | Maximum number of simulation results to return \(1-1000\) |
| `marker` | string | No | Pagination marker from a previous request |
@@ -511,7 +592,7 @@ Simulate whether a user, role, or group is allowed to perform specific AWS actio
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `evaluationResults` | json | Simulation results per action: evalActionName, evalResourceName, evalDecision \(allowed/explicitDeny/implicitDeny\), matchedStatements \(sourcePolicyId, sourcePolicyType\), missingContextValues |
+| `evaluationResults` | json | One result per simulated action. evalDecision is the AGGREGATE, most-restrictive decision across every resource ARN, and evalResourceName is the resource-type ARN template \(e.g. an arn:aws:s3:::BUCKET/KEY shape with the bucket and key left as placeholders\), not a customer ARN. For the verdict on an individual ARN read resourceSpecificResults\[\]: evalResourceName, evalResourceDecision \(allowed/explicitDeny/implicitDeny\), matchedStatements, missingContextValues, permissionsBoundaryAllowed. When concrete resource ARNs are supplied, missing context values appear there rather than at the top level. |
| `isTruncated` | boolean | Whether there are more results available |
| `marker` | string | Pagination marker for the next page of results |
| `count` | number | Number of evaluation results returned |
diff --git a/apps/docs/content/docs/integrations/identity_center.mdx b/apps/docs/content/docs/integrations/identity_center.mdx
index 22b620939ac..3490979219f 100644
--- a/apps/docs/content/docs/integrations/identity_center.mdx
+++ b/apps/docs/content/docs/integrations/identity_center.mdx
@@ -21,9 +21,16 @@ With AWS IAM Identity Center, you can:
- **List permission sets**: Enumerate the available permission sets (e.g., ReadOnly, PowerUser, AdministratorAccess) defined in your Identity Center instance
- **Monitor assignment status**: Poll the provisioning status of create/delete operations, which are asynchronous in AWS
- **List accounts in your organization**: Enumerate all AWS accounts in your AWS Organizations structure to populate access request dropdowns
-- **Manage groups**: List groups and resolve group IDs by display name for group-based access grants
+- **Manage groups**: List groups, resolve group IDs by display name, and enumerate group memberships for group-based access grants
+- **Audit an account's access**: List the assignments on a given AWS account for one permission set, then resolve each principal ID back to the user or group behind it
In Sim, the AWS Identity Center integration is designed to power **TEAM (Temporary Elevated Access Management)** workflows — automated pipelines where users request elevated access, approvers approve or deny it, access is provisioned with a time limit, and auto-revocation removes it when the window expires. This replaces manual console-based access management with auditable, agent-driven workflows that integrate with Slack, email, ticketing systems, and CloudTrail for full traceability.
+
+The same operations support the reverse direction — access review. Starting from an account, an agent can list its assignments, resolve the principals, expand groups into their members, and produce a written report of exactly who can reach that account and through which permission set.
+
+One detail shapes how that review has to be built. AWS requires a permission set ARN alongside the account ID on this call, so *List Assignments For Account* returns only the assignments granted through that one permission set — not every assignment on the account. To cover an account completely, run *List Permission Sets* for the instance first, then call *List Assignments For Account* once per permission set and combine the results. Skipping that loop silently omits access granted through the permission sets you did not ask about.
+
+Two AWS behaviors are worth knowing. Creating and deleting an account assignment are **asynchronous**: both return a request ID, and each has its own status poller — use *Check Assignment Status* for creations and *Check Assignment Deletion Status* for deletions, as the two request-ID types are not interchangeable. And the account-listing operations call AWS Organizations, which is global per partition; the block resolves the correct endpoint for commercial, GovCloud, and China regions automatically.
{/* MANUAL-CONTENT-END */}
@@ -53,7 +60,14 @@ List all AWS IAM Identity Center instances in your account
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `instances` | json | List of Identity Center instances with instanceArn, identityStoreId, name, status, statusReason |
+| `instances` | array | Identity Center instances in the region |
+| ↳ `instanceArn` | string | ARN of the Identity Center instance |
+| ↳ `identityStoreId` | string | Identity Store ID backing the instance |
+| ↳ `name` | string | Instance name |
+| ↳ `status` | string | Instance status |
+| ↳ `statusReason` | string | Explanation when the instance is not ACTIVE |
+| ↳ `ownerAccountId` | string | AWS account that owns the instance |
+| ↳ `createdDate` | string | ISO 8601 date the instance was created |
| `nextToken` | string | Pagination token for the next page of results |
| `count` | number | Number of instances returned |
@@ -68,14 +82,20 @@ List all AWS accounts in your organization
| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
-| `maxResults` | number | No | Maximum number of accounts to return |
+| `maxResults` | number | No | Maximum number of accounts to return \(1-20; the AWS Organizations ceiling\) |
| `nextToken` | string | No | Pagination token from a previous request |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `accounts` | json | List of AWS accounts with id, arn, name, email, status |
+| `accounts` | array | Accounts in the AWS organization |
+| ↳ `id` | string | AWS account ID |
+| ↳ `arn` | string | AWS account ARN |
+| ↳ `name` | string | Account name |
+| ↳ `email` | string | Root email address of the account |
+| ↳ `status` | string | Account status \(e.g., ACTIVE, SUSPENDED\) |
+| ↳ `joinedTimestamp` | string | ISO 8601 date the account joined the organization |
| `nextToken` | string | Pagination token for the next page of results |
| `count` | number | Number of accounts returned |
@@ -115,14 +135,19 @@ List all permission sets defined in an IAM Identity Center instance
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
-| `maxResults` | number | No | Maximum number of permission sets to return |
+| `maxResults` | number | No | Maximum number of permission sets to return \(1-100\) |
| `nextToken` | string | No | Pagination token from a previous request |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `permissionSets` | json | List of permission sets with permissionSetArn, name, description, sessionDuration |
+| `permissionSets` | array | Permission sets defined on the instance |
+| ↳ `permissionSetArn` | string | ARN of the permission set |
+| ↳ `name` | string | Permission set name |
+| ↳ `description` | string | Permission set description |
+| ↳ `sessionDuration` | string | ISO 8601 session duration \(e.g., PT1H\) |
+| ↳ `createdDate` | string | ISO 8601 date the permission set was created |
| `nextToken` | string | Pagination token for the next page of results |
| `count` | number | Number of permission sets returned |
@@ -149,6 +174,34 @@ Look up a user in the Identity Store by email address
| `displayName` | string | Display name of the user |
| `email` | string | Email address of the user |
+### Identity Center Describe User
+
+Resolve an Identity Store user ID to the user behind it. Use to turn the principalId on an account assignment into a name and email.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `identityStoreId` | string | Yes | Identity Store ID \(e.g., d-1234567890\) |
+| `userId` | string | Yes | Identity Store user ID, such as the principalId on a USER account assignment |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `userId` | string | Identity Store user ID |
+| `userName` | string | Username in the Identity Store |
+| `displayName` | string | Display name of the user, or null when the Identity Store omits it |
+| `email` | string | Primary email address, or null when the user has no email attribute |
+| `userStatus` | string | Account status \(ENABLED or DISABLED\), or null when the Identity Store omits it |
+| `title` | string | Job title, or null when the Identity Store omits it |
+| `externalIds` | array | External identity provider IDs linked to the user |
+| ↳ `issuer` | string | Identity provider that issued the ID |
+| ↳ `id` | string | Identifier at the issuer |
+
### Identity Center Get Group
Look up a group in the Identity Store by display name
@@ -171,6 +224,31 @@ Look up a group in the Identity Store by display name
| `displayName` | string | Display name of the group |
| `description` | string | Group description |
+### Identity Center Describe Group
+
+Resolve an Identity Store group ID to the group behind it. Use to turn the principalId on an account assignment into a group name.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `identityStoreId` | string | Yes | Identity Store ID \(e.g., d-1234567890\) |
+| `groupId` | string | Yes | Identity Store group ID, such as the principalId on a GROUP account assignment |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `groupId` | string | Identity Store group ID |
+| `displayName` | string | Display name of the group |
+| `description` | string | Group description |
+| `externalIds` | array | External identity provider IDs linked to the group |
+| ↳ `issuer` | string | Identity provider that issued the ID |
+| ↳ `id` | string | Identifier at the issuer |
+
### Identity Center List Groups
List all groups in the Identity Store
@@ -183,17 +261,50 @@ List all groups in the Identity Store
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
| `identityStoreId` | string | Yes | Identity Store ID \(from the Identity Center instance\) |
-| `maxResults` | number | No | Maximum number of groups to return |
+| `maxResults` | number | No | Maximum number of groups to return \(1-100\) |
| `nextToken` | string | No | Pagination token from a previous request |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `groups` | json | List of groups with groupId, displayName, description |
+| `groups` | array | Groups in the Identity Store |
+| ↳ `groupId` | string | Identity Store group ID \(use as principalId\) |
+| ↳ `displayName` | string | Group display name |
+| ↳ `description` | string | Group description |
+| ↳ `externalIds` | array | External identity provider IDs linked to the group |
+| ↳ `issuer` | string | Identity provider that issued the ID |
+| ↳ `id` | string | Identifier at the issuer |
| `nextToken` | string | Pagination token for the next page of results |
| `count` | number | Number of groups returned |
+### Identity Center List Group Memberships
+
+List the users who belong to an Identity Store group
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `identityStoreId` | string | Yes | Identity Store ID \(e.g., d-1234567890\) |
+| `groupId` | string | Yes | Identity Store group ID whose members to list |
+| `maxResults` | number | No | Maximum number of memberships to return \(1-100\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `memberships` | array | Members of the group |
+| ↳ `membershipId` | string | Identity Store membership ID |
+| ↳ `groupId` | string | Identity Store group ID |
+| ↳ `userId` | string | Identity Store user ID of the member — resolve with Describe User. Null when the member is not a user. |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of memberships returned |
+
### Identity Center Create Account Assignment
Grant a user or group access to an AWS account via a permission set (temporary elevated access)
@@ -248,7 +359,7 @@ Revoke a user or group access to an AWS account by removing a permission set ass
| --------- | ---- | ----------- |
| `message` | string | Status message |
| `status` | string | Deprovisioning status: IN_PROGRESS, FAILED, or SUCCEEDED |
-| `requestId` | string | Request ID to use with Check Assignment Status |
+| `requestId` | string | Request ID to use with Check Assignment Deletion Status |
| `accountId` | string | Target AWS account ID |
| `permissionSetArn` | string | Permission set ARN |
| `principalType` | string | Principal type \(USER or GROUP\) |
@@ -268,7 +379,7 @@ Check the provisioning status of an account assignment creation request
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
-| `requestId` | string | Yes | Request ID returned from Create or Delete Account Assignment |
+| `requestId` | string | Yes | Request ID returned from Create Account Assignment. Deletion request IDs are not accepted — use Check Assignment Deletion Status for those. |
#### Output
@@ -312,9 +423,9 @@ Check the deprovisioning status of an account assignment deletion request
| `failureReason` | string | Reason for failure if status is FAILED |
| `createdDate` | string | Date the request was created |
-### Identity Center List Account Assignments
+### Identity Center List Account Assignments For Principal
-List all account assignments for a specific user or group across all accounts
+List every account and permission set a specific user or group is assigned. Use List Assignments For Account to go the other way, from an account to its principals.
#### Input
@@ -326,14 +437,47 @@ List all account assignments for a specific user or group across all accounts
| `instanceArn` | string | Yes | ARN of the Identity Center instance |
| `principalId` | string | Yes | Identity Store ID of the user or group |
| `principalType` | string | Yes | Type of principal: USER or GROUP |
-| `maxResults` | number | No | Maximum number of assignments to return |
+| `maxResults` | number | No | Maximum number of assignments to return \(1-100\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `assignments` | array | Accounts and permission sets the principal is assigned |
+| ↳ `accountId` | string | AWS account ID |
+| ↳ `permissionSetArn` | string | Permission set ARN |
+| ↳ `principalType` | string | Principal type \(USER or GROUP\) |
+| ↳ `principalId` | string | Identity Store user or group ID |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of assignments returned |
+
+### Identity Center List Assignments For Account
+
+List every principal assigned a specific permission set on a specific AWS account. Use for per-account access reviews.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `instanceArn` | string | Yes | ARN of the Identity Center instance |
+| `accountId` | string | Yes | AWS account ID to list assignments for \(12 digits\) |
+| `permissionSetArn` | string | Yes | ARN of the permission set to list assignments for |
+| `maxResults` | number | No | Maximum number of assignments to return \(1-100\) |
| `nextToken` | string | No | Pagination token from a previous request |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
-| `assignments` | json | List of account assignments with accountId, permissionSetArn, principalType, principalId |
+| `assignments` | array | Principals assigned this permission set on the account |
+| ↳ `accountId` | string | AWS account ID |
+| ↳ `permissionSetArn` | string | Permission set ARN |
+| ↳ `principalType` | string | Principal type \(USER or GROUP\) |
+| ↳ `principalId` | string | Identity Store user or group ID — resolve with Describe User or Describe Group |
| `nextToken` | string | Pagination token for the next page of results |
| `count` | number | Number of assignments returned |
diff --git a/apps/docs/content/docs/integrations/meta.json b/apps/docs/content/docs/integrations/meta.json
index 69d1dac6bfc..df2c5f2d169 100644
--- a/apps/docs/content/docs/integrations/meta.json
+++ b/apps/docs/content/docs/integrations/meta.json
@@ -45,6 +45,7 @@
"clickup-service-account",
"cloudflare",
"cloudformation",
+ "cloudtrail",
"cloudwatch",
"codepipeline",
"confluence",
@@ -206,6 +207,7 @@
"pulse",
"qdrant",
"quartr",
+ "quickbooks",
"quiver",
"rabbitmq",
"railway",
@@ -249,6 +251,7 @@
"sqs",
"square",
"ssh",
+ "ssm",
"stagehand",
"stripe",
"sts",
diff --git a/apps/docs/content/docs/integrations/quickbooks.mdx b/apps/docs/content/docs/integrations/quickbooks.mdx
new file mode 100644
index 00000000000..942579aab7d
--- /dev/null
+++ b/apps/docs/content/docs/integrations/quickbooks.mdx
@@ -0,0 +1,3834 @@
+---
+title: QuickBooks
+description: Manage QuickBooks Online company, transactions, reports, emails, PDFs, and attachments
+---
+
+import { BlockInfoCard } from "@/components/ui/block-info-card"
+
+
+
+{/* MANUAL-CONTENT-START:intro */}
+Connect one QuickBooks Online company per credential. Create an app in the Intuit Developer Portal, register `https:///api/auth/oauth2/callback/quickbooks` as its redirect URI, then enter that app's client ID, client secret, and webhook verifier token in Sim and select its Sandbox or Production environment. Sim encrypts this app configuration on the credential and uses it for authorization, refresh, revocation, and webhook signature verification.
+
+During OAuth, choose the company that the workflow should access. Intuit returns the realm ID in the callback, and Sim verifies that the issued token can read CompanyInfo through that company-scoped API path before binding it to the credential, so you do not enter a realm ID or API host. The `CompanyInfo.Id` field is a separate entity ID and is not used as the realm ID.
+
+Master Data, Sales, and Purchasing transaction reads support **List** and **By ID** modes. List actions return at most one page. Use `nextStartPosition` in another workflow step when `hasMore` is true. Sim does not paginate, retry, or fetch related records automatically.
+
+QuickBooks update actions require the record ID and its current `SyncToken`; provide only the fields you want to change. Sim uses Intuit's documented sparse-update mode where the entity supports it, and otherwise reads the current entity, merges the requested fields, and submits a full update. Use the latest `SyncToken` returned by a read or mutation. Voiding keeps the transaction in QuickBooks with a zeroed financial effect; it is not deletion and requires explicit confirmation. Create actions accept an optional `requestId` that QuickBooks uses for idempotency when the same request may be submitted again.
+
+Sandbox credentials call only Intuit's sandbox API and are suitable for disposable test data. Production credentials call the production API and affect the selected live company.
+
+QuickBooks triggers use Intuit's app-level webhook model. After adding a trigger and deploying the workflow once, copy the generated Webhook URL from the block into the matching Development or Production Webhooks settings for the same Intuit app. Enable the CloudEvents payload format and select every entity and operation needed by your deployed workflows. One Intuit endpoint can serve multiple connected companies; Sim verifies the raw-body `intuit-signature` with that app's encrypted verifier token and routes each event by both Intuit app and OAuth-derived realm ID.
+
+Run Financial Report exposes verified financial statements, aging, balance, sales, and expense reports while preserving QuickBooks' native columns and nested rows. Advanced controls appear only where QuickBooks supports them. Use Read Master Data to discover customer, vendor, account, item, class, and department IDs for report filters. Intuit recommends report periods of six months or less for performance, though Sim does not forbid longer accounting periods.
+
+Document actions can read attachment metadata, add one File or Note attachment, download an attachment file, and download supported transactions as PDFs. Downloaded files are stored as Sim files for downstream blocks. Attachment deletion, bulk upload/download, and bulk email remain outside this version of the block.
+{/* MANUAL-CONTENT-END */}
+
+
+## Usage Instructions
+
+Connect one QuickBooks Online company to manage bounded master-data, sales, purchasing, receivables, payables, accounting, reports, transaction delivery, and document workflows.
+
+
+
+## Actions
+
+### QuickBooks Get Company Info
+
+Get information about the connected QuickBooks Online company
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `company` | json | Verified QuickBooks CompanyInfo object with tax identifiers removed |
+| ↳ `Id` | string | QuickBooks CompanyInfo entity ID \(commonly "1"\); this is not the OAuth realmId |
+| ↳ `SyncToken` | string | CompanyInfo sync token |
+| ↳ `CompanyName` | string | Company display name |
+| ↳ `LegalName` | string | Company legal name |
+| ↳ `CompanyAddr` | json | Company address |
+| ↳ `CustomerCommunicationAddr` | json | Customer communication address |
+| ↳ `LegalAddr` | json | Company legal address |
+| ↳ `PrimaryPhone` | json | Primary phone details |
+| ↳ `Email` | json | Company email details |
+| ↳ `WebAddr` | json | Company website details |
+| ↳ `CompanyStartDate` | string | Company start date |
+| ↳ `Country` | string | Company country code |
+| ↳ `FiscalYearStartMonth` | string | Fiscal year starting month |
+| ↳ `SupportedLanguages` | string | Comma-separated list of languages supported by the company |
+| ↳ `domain` | string | Originating Intuit domain |
+| ↳ `sparse` | boolean | Whether QuickBooks returned a partial representation |
+| ↳ `NameValue` | array | QuickBooks company settings represented as name/value entries |
+| ↳ `MetaData` | json | CompanyInfo creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Read Master Data
+
+List or read one account, class, customer, department, employee, item, or vendor
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recordType` | string | Yes | Master-data entity to read: account, class, customer, department, employee, item, or vendor |
+| `readMode` | string | Yes | Whether to list records or read one record by ID |
+| `recordId` | string | No | QuickBooks record ID, required for by-ID reads |
+| `startPosition` | number | No | One-based position of the first list record to return |
+| `maxResults` | number | No | Number of list records to request \(1–100\) |
+| `activeStatus` | string | No | List records using the QuickBooks default, active, or inactive status |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordType` | string | Master-data record type returned by this action |
+| `item` | json | Single QuickBooks master-data record returned by a by-ID read |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `Name` | string | Account, item, class, or department name |
+| ↳ `SubAccount` | boolean | Whether this is a subaccount |
+| ↳ `ParentRef` | json | Parent account, item, class, or department reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FullyQualifiedName` | string | Hierarchical qualified account, item, class, or department name |
+| ↳ `Classification` | string | Account classification |
+| ↳ `AccountType` | string | Account type |
+| ↳ `AccountSubType` | string | Account subtype |
+| ↳ `CurrentBalance` | number | Account current balance |
+| ↳ `CurrencyRef` | json | Account, customer, or vendor currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `DisplayName` | string | Customer, vendor, or employee display name |
+| ↳ `CompanyName` | string | Customer or vendor company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `Taxable` | boolean | Taxable status for the customer or item |
+| ↳ `PrimaryEmailAddr` | json | Customer, vendor, or employee primary email address |
+| ↳ `PrimaryPhone` | json | Customer, vendor, or employee primary phone number |
+| ↳ `BillAddr` | json | Customer or vendor billing address |
+| ↳ `ShipAddr` | json | Customer shipping address |
+| ↳ `Balance` | number | Customer or vendor balance |
+| ↳ `PrintOnCheckName` | string | Vendor or employee name printed on checks |
+| ↳ `Vendor1099` | boolean | Whether the vendor is tracked for 1099 reporting |
+| ↳ `AcctNum` | string | Vendor account number |
+| ↳ `Description` | string | Item sales description |
+| ↳ `UnitPrice` | number | Item sale price |
+| ↳ `Type` | string | Item type |
+| ↳ `IncomeAccountRef` | json | Item income account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ExpenseAccountRef` | json | Item expense account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PurchaseDesc` | string | Item purchase description |
+| ↳ `PurchaseCost` | number | Item purchase cost |
+| ↳ `AssetAccountRef` | json | Inventory asset account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `TrackQtyOnHand` | boolean | Whether QuickBooks tracks quantity on hand |
+| ↳ `QtyOnHand` | number | Current quantity on hand |
+| ↳ `InvStartDate` | string | Inventory tracking start date |
+| ↳ `PrimaryAddr` | json | Employee primary address |
+| ↳ `BillableTime` | boolean | Whether employee time is billable |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+| ↳ `SubClass` | boolean | Whether the Class is nested under another Class |
+| ↳ `SubDepartment` | boolean | Whether the Department is nested under another Department |
+| `items` | array | QuickBooks master-data records returned by a list read |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `Name` | string | Account, item, class, or department name |
+| ↳ `SubAccount` | boolean | Whether this is a subaccount |
+| ↳ `ParentRef` | json | Parent account, item, class, or department reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FullyQualifiedName` | string | Hierarchical qualified account, item, class, or department name |
+| ↳ `Classification` | string | Account classification |
+| ↳ `AccountType` | string | Account type |
+| ↳ `AccountSubType` | string | Account subtype |
+| ↳ `CurrentBalance` | number | Account current balance |
+| ↳ `CurrencyRef` | json | Account, customer, or vendor currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `DisplayName` | string | Customer, vendor, or employee display name |
+| ↳ `CompanyName` | string | Customer or vendor company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `Taxable` | boolean | Taxable status for the customer or item |
+| ↳ `PrimaryEmailAddr` | json | Customer, vendor, or employee primary email address |
+| ↳ `PrimaryPhone` | json | Customer, vendor, or employee primary phone number |
+| ↳ `BillAddr` | json | Customer or vendor billing address |
+| ↳ `ShipAddr` | json | Customer shipping address |
+| ↳ `Balance` | number | Customer or vendor balance |
+| ↳ `PrintOnCheckName` | string | Vendor or employee name printed on checks |
+| ↳ `Vendor1099` | boolean | Whether the vendor is tracked for 1099 reporting |
+| ↳ `AcctNum` | string | Vendor account number |
+| ↳ `Description` | string | Item sales description |
+| ↳ `UnitPrice` | number | Item sale price |
+| ↳ `Type` | string | Item type |
+| ↳ `IncomeAccountRef` | json | Item income account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ExpenseAccountRef` | json | Item expense account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PurchaseDesc` | string | Item purchase description |
+| ↳ `PurchaseCost` | number | Item purchase cost |
+| ↳ `AssetAccountRef` | json | Inventory asset account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `TrackQtyOnHand` | boolean | Whether QuickBooks tracks quantity on hand |
+| ↳ `QtyOnHand` | number | Current quantity on hand |
+| ↳ `InvStartDate` | string | Inventory tracking start date |
+| ↳ `PrimaryAddr` | json | Employee primary address |
+| ↳ `BillableTime` | boolean | Whether employee time is billable |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+| ↳ `SubClass` | boolean | Whether the Class is nested under another Class |
+| ↳ `SubDepartment` | boolean | Whether the Department is nested under another Department |
+| `recordVersion` | string | Display-safe alias for the native SyncToken on a by-ID record |
+| `startPosition` | number | One-based position of the first record in this page |
+| `maxResults` | number | Actual number of records returned in this page |
+| `nextStartPosition` | number | Position to use when explicitly requesting the next page |
+| `hasMore` | boolean | Conservative indication that another page may exist |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Create Customer
+
+Create a customer in the connected QuickBooks Online company
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `displayName` | string | No | Unique customer display name. Required unless givenName or familyName is supplied |
+| `companyName` | string | No | Customer company name |
+| `givenName` | string | No | Customer given name |
+| `familyName` | string | No | Customer family name |
+| `primaryEmail` | string | No | Customer primary email address |
+| `primaryPhone` | string | No | Customer primary phone number |
+| `billingAddress` | json | No | Customer billing address |
+| `shippingAddress` | json | No | Customer shipping address |
+| `taxable` | boolean | No | Whether sales to this customer are taxable |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created QuickBooks Customer record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Customer display name |
+| ↳ `CompanyName` | string | Customer company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `Taxable` | boolean | Whether the customer is taxable |
+| ↳ `PrimaryEmailAddr` | json | Customer primary email address |
+| ↳ `PrimaryPhone` | json | Customer primary phone number |
+| ↳ `BillAddr` | json | Customer billing address |
+| ↳ `ShipAddr` | json | Customer shipping address |
+| ↳ `Balance` | number | Customer balance |
+| ↳ `CurrencyRef` | json | Customer currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Update Customer
+
+Sparse-update a customer in the connected QuickBooks Online company
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | ID of the customer to update |
+| `syncToken` | string | Yes | Current customer sync token |
+| `displayName` | string | No | Replacement customer display name |
+| `companyName` | string | No | Replacement customer company name |
+| `givenName` | string | No | Replacement customer given name |
+| `familyName` | string | No | Replacement customer family name |
+| `primaryEmail` | string | No | Replacement primary email address |
+| `primaryPhone` | string | No | Replacement primary phone number |
+| `billingAddress` | json | No | Replacement billing address |
+| `shippingAddress` | json | No | Replacement shipping address |
+| `taxable` | boolean | No | Whether sales to this customer are taxable |
+| `activeStatus` | string | No | Customer status change: unchanged, active, or inactive |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated QuickBooks Customer record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Customer display name |
+| ↳ `CompanyName` | string | Customer company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `Taxable` | boolean | Whether the customer is taxable |
+| ↳ `PrimaryEmailAddr` | json | Customer primary email address |
+| ↳ `PrimaryPhone` | json | Customer primary phone number |
+| ↳ `BillAddr` | json | Customer billing address |
+| ↳ `ShipAddr` | json | Customer shipping address |
+| ↳ `Balance` | number | Customer balance |
+| ↳ `CurrencyRef` | json | Customer currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Create Employee
+
+Create a non-payroll employee profile in the connected QuickBooks Online company
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `displayName` | string | No | Unique employee display name. When omitted QuickBooks derives it from the supplied name components, and it is read-only when QuickBooks Payroll is enabled |
+| `givenName` | string | No | Employee given name. At least one of givenName or familyName is required |
+| `familyName` | string | No | Employee family name. At least one of givenName or familyName is required |
+| `primaryEmail` | string | No | Employee primary email address |
+| `primaryPhone` | string | No | Employee primary phone number |
+| `primaryAddress` | json | No | Employee primary address |
+| `printOnCheckName` | string | No | Employee name printed on checks |
+| `billableTime` | boolean | No | Whether employee time is billable |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created QuickBooks Employee record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Employee display name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `PrintOnCheckName` | string | Employee name printed on checks |
+| ↳ `PrimaryEmailAddr` | json | Employee primary email address |
+| ↳ `PrimaryPhone` | json | Employee primary phone number |
+| ↳ `PrimaryAddr` | json | Employee primary address |
+| ↳ `BillableTime` | boolean | Whether employee time is billable |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+
+### QuickBooks Update Employee
+
+Read, merge, and full-update a non-payroll employee profile
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `employeeId` | string | Yes | ID of the employee to update |
+| `syncToken` | string | Yes | Current employee sync token |
+| `displayName` | string | No | Replacement employee display name |
+| `givenName` | string | No | Replacement employee given name |
+| `familyName` | string | No | Replacement employee family name |
+| `primaryEmail` | string | No | Replacement employee primary email address |
+| `primaryPhone` | string | No | Replacement employee primary phone number |
+| `primaryAddress` | json | No | Replacement employee primary address |
+| `printOnCheckName` | string | No | Replacement employee name printed on checks |
+| `billableTime` | boolean | No | Whether employee time is billable |
+| `activeStatus` | string | No | Employee status change: unchanged, active, or inactive |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated QuickBooks Employee record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Employee display name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `PrintOnCheckName` | string | Employee name printed on checks |
+| ↳ `PrimaryEmailAddr` | json | Employee primary email address |
+| ↳ `PrimaryPhone` | json | Employee primary phone number |
+| ↳ `PrimaryAddr` | json | Employee primary address |
+| ↳ `BillableTime` | boolean | Whether employee time is billable |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+
+### QuickBooks Create Vendor
+
+Create a vendor in the connected QuickBooks Online company
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `displayName` | string | No | Unique vendor display name. Required unless givenName or familyName is supplied |
+| `companyName` | string | No | Vendor company name |
+| `givenName` | string | No | Vendor given name |
+| `familyName` | string | No | Vendor family name |
+| `primaryEmail` | string | No | Vendor primary email address |
+| `primaryPhone` | string | No | Vendor primary phone number |
+| `billingAddress` | json | No | Vendor billing address |
+| `printOnCheckName` | string | No | Name to print on checks |
+| `accountNumber` | string | No | Vendor account number |
+| `vendor1099` | boolean | No | Whether the vendor is tracked for 1099 reporting |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created QuickBooks Vendor record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Vendor display name |
+| ↳ `CompanyName` | string | Vendor company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `PrintOnCheckName` | string | Name printed on checks |
+| ↳ `Vendor1099` | boolean | Whether the vendor is tracked for 1099 reporting |
+| ↳ `PrimaryEmailAddr` | json | Vendor primary email address |
+| ↳ `PrimaryPhone` | json | Vendor primary phone number |
+| ↳ `BillAddr` | json | Vendor billing address |
+| ↳ `AcctNum` | string | Vendor account number |
+| ↳ `Balance` | number | Vendor balance |
+| ↳ `CurrencyRef` | json | Vendor currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Update Vendor
+
+Read, merge, and full-update a vendor in QuickBooks Online
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorId` | string | Yes | ID of the vendor to update |
+| `syncToken` | string | Yes | Current vendor sync token |
+| `displayName` | string | No | Replacement vendor display name |
+| `companyName` | string | No | Replacement vendor company name |
+| `givenName` | string | No | Replacement vendor given name |
+| `familyName` | string | No | Replacement vendor family name |
+| `primaryEmail` | string | No | Replacement primary email address |
+| `primaryPhone` | string | No | Replacement primary phone number |
+| `billingAddress` | json | No | Replacement billing address |
+| `printOnCheckName` | string | No | Replacement name to print on checks |
+| `accountNumber` | string | No | Replacement vendor account number |
+| `vendor1099` | boolean | No | Whether the vendor is tracked for 1099 reporting |
+| `activeStatus` | string | No | Vendor status change: unchanged, active, or inactive |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated QuickBooks Vendor record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `DisplayName` | string | Vendor display name |
+| ↳ `CompanyName` | string | Vendor company name |
+| ↳ `GivenName` | string | Given name |
+| ↳ `FamilyName` | string | Family name |
+| ↳ `PrintOnCheckName` | string | Name printed on checks |
+| ↳ `Vendor1099` | boolean | Whether the vendor is tracked for 1099 reporting |
+| ↳ `PrimaryEmailAddr` | json | Vendor primary email address |
+| ↳ `PrimaryPhone` | json | Vendor primary phone number |
+| ↳ `BillAddr` | json | Vendor billing address |
+| ↳ `AcctNum` | string | Vendor account number |
+| ↳ `Balance` | number | Vendor balance |
+| ↳ `CurrencyRef` | json | Vendor currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Create Item
+
+Create a Service or Non-inventory item in QuickBooks Online
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `name` | string | Yes | Unique item name |
+| `itemType` | string | Yes | Writable item type: service or non_inventory |
+| `incomeAccountId` | string | No | Sales of Product Income account ID recording proceeds from the sale. Intuit requires it for Service items except in France locales |
+| `description` | string | No | Sales description |
+| `unitPrice` | number | No | Sales price per unit |
+| `purchaseDescription` | string | No | Purchase description |
+| `purchaseCost` | number | No | Purchase cost per unit |
+| `expenseAccountId` | string | No | Cost of Goods Sold account ID used to pay the vendor for this item. Intuit requires it for Service and Non-inventory items except in France locales |
+| `taxable` | boolean | No | Whether the item is taxable |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created QuickBooks Item record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `Name` | string | Item name |
+| ↳ `Description` | string | Item sales description |
+| ↳ `FullyQualifiedName` | string | Hierarchical qualified item name |
+| ↳ `Taxable` | boolean | Whether the item is taxable |
+| ↳ `UnitPrice` | number | Item sale price |
+| ↳ `Type` | string | Item type |
+| ↳ `IncomeAccountRef` | json | Item income account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ExpenseAccountRef` | json | Item expense account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PurchaseDesc` | string | Item purchase description |
+| ↳ `PurchaseCost` | number | Item purchase cost |
+| ↳ `AssetAccountRef` | json | Inventory asset account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `TrackQtyOnHand` | boolean | Whether QuickBooks tracks quantity on hand |
+| ↳ `QtyOnHand` | number | Current quantity on hand |
+| ↳ `InvStartDate` | string | Inventory tracking start date |
+| ↳ `ParentRef` | json | Parent item or category reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Update Item
+
+Read, merge, and full-update an item without changing its type
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `itemId` | string | Yes | ID of the item to update |
+| `syncToken` | string | Yes | Current item sync token |
+| `name` | string | No | Replacement item name |
+| `incomeAccountId` | string | No | Replacement income account ID |
+| `description` | string | No | Replacement sales description |
+| `unitPrice` | number | No | Replacement sales price per unit |
+| `purchaseDescription` | string | No | Replacement purchase description |
+| `purchaseCost` | number | No | Replacement purchase cost per unit |
+| `expenseAccountId` | string | No | Replacement expense account ID |
+| `taxable` | boolean | No | Whether the item is taxable |
+| `activeStatus` | string | No | Item status change: unchanged, active, or inactive |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated QuickBooks Item record |
+| ↳ `Id` | string | QuickBooks entity ID |
+| ↳ `SyncToken` | string | Entity sync token |
+| ↳ `Active` | boolean | Whether the entity is active |
+| ↳ `MetaData` | json | Entity creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `Name` | string | Item name |
+| ↳ `Description` | string | Item sales description |
+| ↳ `FullyQualifiedName` | string | Hierarchical qualified item name |
+| ↳ `Taxable` | boolean | Whether the item is taxable |
+| ↳ `UnitPrice` | number | Item sale price |
+| ↳ `Type` | string | Item type |
+| ↳ `IncomeAccountRef` | json | Item income account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ExpenseAccountRef` | json | Item expense account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PurchaseDesc` | string | Item purchase description |
+| ↳ `PurchaseCost` | number | Item purchase cost |
+| ↳ `AssetAccountRef` | json | Inventory asset account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `TrackQtyOnHand` | boolean | Whether QuickBooks tracks quantity on hand |
+| ↳ `QtyOnHand` | number | Current quantity on hand |
+| ↳ `InvStartDate` | string | Inventory tracking start date |
+| ↳ `ParentRef` | json | Parent item or category reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+
+### QuickBooks Read Sales Transactions
+
+List or read one estimate, invoice, sales receipt, payment, credit memo, or refund receipt
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionType` | string | Yes | Sales transaction type to read |
+| `readMode` | string | Yes | Whether to list transactions or read one transaction by ID |
+| `transactionId` | string | No | QuickBooks transaction ID, required for by-ID reads |
+| `startPosition` | number | No | One-based position of the first list record to return |
+| `maxResults` | number | No | Number of list records to request \(1–100\) |
+| `startDate` | string | No | List transactions on or after this date in YYYY-MM-DD format |
+| `endDate` | string | No | List transactions on or before this date in YYYY-MM-DD format |
+| `customerId` | string | No | List transactions for one QuickBooks customer ID |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `transactionType` | string | Sales transaction type returned |
+| `item` | json | Single native QuickBooks sales transaction |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `items` | array | Native QuickBooks sales transactions |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `recordVersion` | string | Display-safe alias for the native SyncToken on a by-ID transaction |
+| `startPosition` | number | One-based position of the first item in this response |
+| `maxResults` | number | Actual number of items reported for this response |
+| `nextStartPosition` | number | Position to use when explicitly requesting the next page |
+| `hasMore` | boolean | Conservative indication that another page may exist |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Create Estimate
+
+Create an estimate with bounded item and description lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer receiving the estimate |
+| `lines` | json | Yes | Bounded item and description lines |
+| `transactionDate` | string | No | Estimate date in YYYY-MM-DD format |
+| `expirationDate` | string | No | Estimate expiration date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional estimate number |
+| `privateNote` | string | No | Internal estimate note |
+| `customerMemo` | string | No | Customer-facing estimate memo |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks Estimate |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Estimate
+
+Sparse-update an estimate using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Estimate ID to update |
+| `syncToken` | string | Yes | Current estimate sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `lines` | json | No | Complete replacement set of estimate lines: any existing line omitted here is deleted from the estimate |
+| `transactionDate` | string | No | Replacement estimate date in YYYY-MM-DD format |
+| `expirationDate` | string | No | Replacement expiration date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement estimate number |
+| `privateNote` | string | No | Replacement internal note |
+| `customerMemo` | string | No | Replacement customer-facing memo |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Estimate |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Invoice
+
+Create an invoice without emailing or collecting payment
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer receiving the invoice |
+| `lines` | json | Yes | Bounded item and description lines |
+| `transactionDate` | string | No | Invoice date in YYYY-MM-DD format |
+| `dueDate` | string | No | Invoice due date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional invoice number |
+| `privateNote` | string | No | Internal invoice note |
+| `customerMemo` | string | No | Customer-facing invoice memo |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks Invoice |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Invoice
+
+Sparse-update an invoice using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Invoice ID to update |
+| `syncToken` | string | Yes | Current invoice sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `lines` | json | No | Complete replacement set of invoice lines: any existing line omitted here is deleted from the invoice |
+| `transactionDate` | string | No | Replacement invoice date in YYYY-MM-DD format |
+| `dueDate` | string | No | Replacement due date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement invoice number |
+| `privateNote` | string | No | Replacement internal note |
+| `customerMemo` | string | No | Replacement customer-facing memo |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Invoice |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Void Invoice
+
+Void an invoice after explicit confirmation
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Invoice ID to void |
+| `syncToken` | string | Yes | Current invoice sync token |
+| `confirmVoid` | boolean | Yes | Explicit confirmation that the invoice should be voided |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `voided` | boolean | Whether QuickBooks voided the transaction |
+| `record` | json | Voided native QuickBooks Invoice |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Sales Receipt
+
+Create a sales receipt for a completed customer sale
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer for the sales receipt |
+| `lines` | json | Yes | Bounded item and description lines |
+| `transactionDate` | string | No | Sales receipt date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional sales receipt number |
+| `privateNote` | string | No | Internal sales receipt note |
+| `customerMemo` | string | No | Customer-facing sales receipt memo |
+| `paymentMethodId` | string | No | QuickBooks payment method ID |
+| `paymentReferenceNumber` | string | No | Payment reference number |
+| `depositAccountId` | string | No | QuickBooks deposit account ID |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks SalesReceipt |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Sales Receipt
+
+Sparse-update a sales receipt using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Sales receipt ID to update |
+| `syncToken` | string | Yes | Current sales receipt sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `lines` | json | No | Complete replacement set of sales receipt lines: any existing line omitted here is deleted from the sales receipt |
+| `transactionDate` | string | No | Replacement receipt date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement sales receipt number |
+| `privateNote` | string | No | Replacement internal note |
+| `customerMemo` | string | No | Replacement customer-facing memo |
+| `paymentMethodId` | string | No | Replacement payment method ID |
+| `paymentReferenceNumber` | string | No | Replacement payment reference number |
+| `depositAccountId` | string | No | Replacement deposit account ID |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks SalesReceipt |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Customer Payment
+
+Record a customer payment with optional bounded invoice allocations
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer making the payment |
+| `totalAmount` | number | Yes | Positive total payment amount |
+| `transactionDate` | string | No | Payment date in YYYY-MM-DD format |
+| `privateNote` | string | No | Internal payment note |
+| `paymentReferenceNumber` | string | No | Payment reference number such as a check number |
+| `paymentMethodId` | string | No | QuickBooks payment method ID |
+| `depositAccountId` | string | No | QuickBooks deposit account ID |
+| `invoiceAllocations` | json | No | Up to 100 invoice allocations with invoiceId and positive amount |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks Payment |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Customer Payment
+
+Read, merge, and full-update a customer payment using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `paymentId` | string | Yes | Payment ID to update |
+| `syncToken` | string | Yes | Current payment sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `totalAmount` | number | No | Replacement positive payment total |
+| `transactionDate` | string | No | Replacement payment date in YYYY-MM-DD format |
+| `privateNote` | string | No | Replacement internal note |
+| `paymentReferenceNumber` | string | No | Replacement payment reference number |
+| `paymentMethodId` | string | No | Replacement payment method ID |
+| `depositAccountId` | string | No | Replacement deposit account ID |
+| `invoiceAllocations` | json | No | Bounded invoice allocations to apply. Each entry sets the amount applied to that invoice; invoices already applied on the payment and not listed here keep their current amounts |
+| `unapplyOmittedInvoices` | boolean | No | Replace the payment allocations outright. Requires a non-empty invoiceAllocations list; every invoice not listed is UNAPPLIED and returns to open |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Payment |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Void Customer Payment
+
+Void a customer payment after explicit confirmation
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Payment ID to void |
+| `syncToken` | string | Yes | Current payment sync token |
+| `confirmVoid` | boolean | Yes | Explicit confirmation that the payment should be voided |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `voided` | boolean | Whether QuickBooks voided the transaction |
+| `record` | json | Voided native QuickBooks Payment |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Credit Memo
+
+Create a customer credit memo with bounded sales lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer receiving the credit memo |
+| `lines` | json | Yes | Bounded item and description lines |
+| `transactionDate` | string | No | Credit memo date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional credit memo number |
+| `privateNote` | string | No | Internal credit memo note |
+| `customerMemo` | string | No | Customer-facing credit memo memo |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks CreditMemo |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Credit Memo
+
+Read, merge, and full-update a credit memo using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Credit memo ID to update |
+| `syncToken` | string | Yes | Current credit memo sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `lines` | json | No | Complete replacement set of credit memo lines: any existing line omitted here is deleted from the credit memo |
+| `transactionDate` | string | No | Replacement credit memo date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement credit memo number |
+| `privateNote` | string | No | Replacement internal note |
+| `customerMemo` | string | No | Replacement customer-facing memo |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks CreditMemo |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Refund Receipt
+
+Create a customer refund receipt against a required deposit account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `customerId` | string | Yes | Customer receiving the refund |
+| `lines` | json | Yes | Bounded item and description lines |
+| `depositAccountId` | string | Yes | QuickBooks bank account funding the refund |
+| `transactionDate` | string | No | Refund receipt date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional refund receipt number |
+| `privateNote` | string | No | Internal refund receipt note |
+| `customerMemo` | string | No | Customer-facing refund memo |
+| `paymentMethodId` | string | No | QuickBooks payment method ID |
+| `paymentReferenceNumber` | string | No | Refund payment reference number |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks RefundReceipt |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Refund Receipt
+
+Read, merge, and full-update a refund receipt using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionId` | string | Yes | Refund receipt ID to update |
+| `syncToken` | string | Yes | Current refund receipt sync token |
+| `customerId` | string | No | Replacement customer ID |
+| `lines` | json | No | Complete replacement set of refund receipt lines: any existing line omitted here is deleted from the refund receipt |
+| `transactionDate` | string | No | Replacement refund date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement refund receipt number |
+| `privateNote` | string | No | Replacement internal note |
+| `customerMemo` | string | No | Replacement customer-facing memo |
+| `paymentMethodId` | string | No | Replacement payment method ID |
+| `paymentReferenceNumber` | string | No | Replacement payment reference number |
+| `depositAccountId` | string | No | Replacement deposit account ID |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks RefundReceipt |
+| ↳ `Id` | string | QuickBooks sales transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Invoice due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Customer payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks transaction lines |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Read Purchasing Transactions
+
+List or read one purchase order, bill, bill payment, vendor credit, or purchase
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionType` | string | Yes | Purchasing transaction type to read |
+| `readMode` | string | Yes | Whether to list transactions or read one transaction by ID |
+| `transactionId` | string | No | QuickBooks transaction ID, required for by-ID reads |
+| `startPosition` | number | No | One-based position of the first list record to return |
+| `maxResults` | number | No | Number of list records to request \(1–100\) |
+| `startDate` | string | No | List transactions on or after this date in YYYY-MM-DD format |
+| `endDate` | string | No | List transactions on or before this date in YYYY-MM-DD format |
+| `vendorId` | string | No | List transactions for one supported QuickBooks vendor ID |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `transactionType` | string | Purchasing transaction type returned |
+| `item` | json | Single native QuickBooks purchasing transaction |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `items` | array | Native QuickBooks purchasing transactions |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `recordVersion` | string | Display-safe alias for the native SyncToken on a by-ID transaction |
+| `startPosition` | number | One-based position of the first item in this response |
+| `maxResults` | number | Actual number of items reported for this response |
+| `nextStartPosition` | number | Position to use when explicitly requesting the next page |
+| `hasMore` | boolean | Conservative indication that another page may exist |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Create Purchase Order
+
+Create a purchase order with bounded expense lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorId` | string | Yes | Purchase-order vendor ID |
+| `apAccountId` | string | Yes | Accounts-payable account ID |
+| `lines` | json | Yes | Bounded account-based or item-based expense lines |
+| `transactionDate` | string | No | Purchase-order date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional purchase-order number |
+| `privateNote` | string | No | Internal purchase-order note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks PurchaseOrder |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Purchase Order
+
+Read, merge, and full-update purchase-order header fields
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `purchaseOrderId` | string | Yes | Purchase Order ID to update |
+| `syncToken` | string | Yes | Current purchase-order sync token |
+| `vendorId` | string | No | Replacement vendor ID |
+| `apAccountId` | string | No | Replacement accounts-payable account ID |
+| `transactionDate` | string | No | Replacement date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement purchase-order number |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks PurchaseOrder |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Bill
+
+Create a vendor bill with optional Purchase Order line links without paying it
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorId` | string | Yes | Bill vendor ID |
+| `lines` | json | Yes | Bounded account-based or item-based expense lines with optional paired Purchase Order and line IDs |
+| `apAccountId` | string | No | Optional accounts-payable account ID |
+| `transactionDate` | string | No | Bill date in YYYY-MM-DD format |
+| `dueDate` | string | No | Bill due date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional bill number |
+| `privateNote` | string | No | Internal bill note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `linkingRequested` | boolean | Whether any Purchase Order line links were requested |
+| `linkingSucceeded` | boolean | Whether QuickBooks returned every requested Purchase Order line link |
+| `linkedLines` | array | Requested Purchase Order line links confirmed by QuickBooks |
+| ↳ `purchaseOrderId` | string | Requested Purchase Order ID |
+| ↳ `purchaseOrderLineId` | string | Requested Purchase Order line ID |
+| ↳ `billLineId` | string | Created Bill line ID carrying the confirmed link |
+| `missingLinks` | array | Requested Purchase Order line links omitted by QuickBooks |
+| ↳ `purchaseOrderId` | string | Requested Purchase Order ID |
+| ↳ `purchaseOrderLineId` | string | Requested Purchase Order line ID |
+| `linkingWarning` | string | Warning that the Bill was created without every requested Purchase Order link |
+| `record` | json | Created native QuickBooks Bill |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Bill
+
+Read, merge, and full-update bill header fields using its current sync token
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `billId` | string | Yes | Bill ID to update |
+| `syncToken` | string | Yes | Current bill sync token |
+| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
+| `apAccountId` | string | No | Replacement accounts-payable account ID |
+| `transactionDate` | string | No | Replacement bill date in YYYY-MM-DD format |
+| `dueDate` | string | No | Replacement due date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement bill number |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Bill |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Bill Payment
+
+Record a check or credit-card payment allocated to one or more bills
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorId` | string | Yes | Vendor whose bills are being paid |
+| `totalAmount` | number | Yes | Positive total payment amount |
+| `paymentType` | string | Yes | Check or credit-card payment type |
+| `paymentAccountId` | string | Yes | Bank or credit-card account ID matching the payment type |
+| `billAllocations` | json | No | Optional bounded Bill-only allocations; any unallocated amount becomes vendor credit |
+| `transactionDate` | string | No | Payment date in YYYY-MM-DD format |
+| `privateNote` | string | No | Internal payment note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks BillPayment |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Bill Payment
+
+Read, merge, and full-update a BillPayment without changing allocations
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `billPaymentId` | string | Yes | BillPayment ID to update |
+| `syncToken` | string | Yes | Current BillPayment sync token |
+| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
+| `transactionDate` | string | No | Replacement payment date in YYYY-MM-DD format |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks BillPayment |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Vendor Credit
+
+Create a vendor credit without applying it to a bill
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorId` | string | Yes | Vendor issuing the credit |
+| `lines` | json | Yes | Bounded account-based or item-based expense lines |
+| `apAccountId` | string | No | Optional accounts-payable account ID |
+| `transactionDate` | string | No | Credit date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional vendor-credit number |
+| `privateNote` | string | No | Internal vendor-credit note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks VendorCredit |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Vendor Credit
+
+Read, merge, and full-update vendor-credit header fields
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `vendorCreditId` | string | Yes | VendorCredit ID to update |
+| `syncToken` | string | Yes | Current vendor-credit sync token |
+| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
+| `apAccountId` | string | No | Replacement accounts-payable account ID |
+| `transactionDate` | string | No | Replacement date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement vendor-credit number |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks VendorCredit |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Purchase
+
+Record a cash, check, or credit-card purchase with bounded expense lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `paymentType` | string | Yes | Cash, check, or credit-card purchase type |
+| `paymentAccountId` | string | Yes | Bank or credit-card account ID matching the purchase type |
+| `lines` | json | Yes | Bounded account-based or item-based expense lines |
+| `vendorId` | string | No | Optional vendor payee ID |
+| `transactionDate` | string | No | Purchase date in YYYY-MM-DD format |
+| `paymentReference` | string | No | Optional transaction reference number, such as a check number, sent as the purchase DocNumber |
+| `privateNote` | string | No | Internal purchase note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks Purchase |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Purchase
+
+Read, merge, and full-update purchase header fields without changing lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `purchaseId` | string | Yes | Purchase ID to update |
+| `syncToken` | string | Yes | Current purchase sync token |
+| `vendorId` | string | No | Replacement vendor payee ID |
+| `transactionDate` | string | No | Replacement purchase date in YYYY-MM-DD format |
+| `paymentReference` | string | No | Replacement transaction reference number, such as a check number, sent as the purchase DocNumber |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Purchase |
+| ↳ `Id` | string | QuickBooks purchasing transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Bill due date |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks expense or allocation lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Read Accounting Transactions
+
+List or read one journal entry, deposit, or transfer
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionType` | string | Yes | Accounting transaction type to read |
+| `readMode` | string | Yes | Whether to list transactions or read one transaction by ID |
+| `transactionId` | string | No | QuickBooks transaction ID, required for by-ID reads |
+| `startPosition` | number | No | One-based position of the first list record to return |
+| `maxResults` | number | No | Number of list records to request \(1–100\) |
+| `startDate` | string | No | List transactions on or after this date in YYYY-MM-DD format |
+| `endDate` | string | No | List transactions on or before this date in YYYY-MM-DD format |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `transactionType` | string | Accounting transaction type returned |
+| `item` | json | Single native QuickBooks accounting transaction |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `items` | array | Native QuickBooks accounting transactions |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| `recordVersion` | string | Display-safe alias for the native SyncToken on a by-ID transaction |
+| `startPosition` | number | One-based position of the first item in this response |
+| `maxResults` | number | Actual number of items reported for this response |
+| `nextStartPosition` | number | Position to use when explicitly requesting the next page |
+| `hasMore` | boolean | Conservative indication that another page may exist |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Create Journal Entry
+
+Post a balanced journal entry after explicit confirmation
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `lines` | json | Yes | Two to 100 balanced debit and credit lines |
+| `confirmPosting` | boolean | Yes | Explicit confirmation that this journal entry should be posted |
+| `transactionDate` | string | No | Journal-entry date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Optional journal-entry number |
+| `privateNote` | string | No | Internal journal-entry note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks JournalEntry |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Journal Entry
+
+Sparse-update journal-entry header fields after explicit confirmation
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `journalEntryId` | string | Yes | Journal Entry ID to update |
+| `syncToken` | string | Yes | Current journal-entry sync token |
+| `confirmPosting` | boolean | Yes | Explicit confirmation that this journal-entry update should be posted |
+| `transactionDate` | string | No | Replacement date in YYYY-MM-DD format |
+| `documentNumber` | string | No | Replacement journal-entry number |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks JournalEntry |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Create Deposit
+
+Create a deposit with bounded account lines
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `depositAccountId` | string | Yes | Bank or asset account receiving the deposit |
+| `lines` | json | Yes | One to 100 account-based deposit lines |
+| `transactionDate` | string | No | Deposit date in YYYY-MM-DD format |
+| `privateNote` | string | No | Internal deposit note |
+| `requestId` | string | No | Optional Intuit idempotency request ID, up to 50 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Created native QuickBooks Deposit |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Update Deposit
+
+Sparse-update deposit header fields using the current sync token and destination account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `depositId` | string | Yes | Deposit ID to update |
+| `syncToken` | string | Yes | Current deposit sync token |
+| `depositAccountId` | string | Yes | Current QuickBooks account receiving the deposit |
+| `transactionDate` | string | No | Replacement date in YYYY-MM-DD format |
+| `privateNote` | string | No | Replacement internal note |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `recordId` | string | ID of the created or updated QuickBooks entity |
+| `syncToken` | string | Native QuickBooks SyncToken returned by the mutation |
+| `recordVersion` | string | Latest QuickBooks record version required for a subsequent update; this is the native SyncToken under a display-safe name |
+| `time` | string | QuickBooks response timestamp |
+| `record` | json | Updated native QuickBooks Deposit |
+| ↳ `Id` | string | QuickBooks accounting transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `Adjustment` | boolean | Whether the journal entry is an adjusting entry |
+| ↳ `DepositToAccountRef` | json | Account receiving a deposit |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `FromAccountRef` | json | Transfer source account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `ToAccountRef` | json | Transfer destination account |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks journal or deposit lines |
+| ↳ `Amount` | number | Transfer amount |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+
+### QuickBooks Run Financial Report
+
+Run a fixed QuickBooks financial report with verified accountant-focused filters
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `reportType` | string | Yes | Fixed QuickBooks financial report to run |
+| `startDate` | string | No | Report start date in YYYY-MM-DD format; Intuit recommends periods of six months or less for performance |
+| `endDate` | string | No | Report end or as-of date in YYYY-MM-DD format |
+| `accountingMethod` | string | No | Use the QuickBooks default, cash basis, or accrual basis |
+| `summarizeBy` | string | No | Time period or business dimension used to summarize report columns |
+| `customerId` | string | No | Single QuickBooks customer ID filter |
+| `vendorId` | string | No | Single QuickBooks vendor ID filter |
+| `accountId` | string | No | Single QuickBooks account ID filter |
+| `itemId` | string | No | Single QuickBooks item ID filter |
+| `classId` | string | No | Single QuickBooks class ID filter |
+| `departmentId` | string | No | Single QuickBooks department ID filter |
+| `agingMethod` | string | No | Age open balances from the report date or current date |
+| `agingDays` | number | No | Positive number of days in each aging period |
+| `transactionType` | string | No | Transaction type filter for Transaction List |
+| `groupBy` | string | No | Grouping dimension for Transaction List |
+| `accountsPayablePaid` | string | No | Accounts-payable paid status for Transaction List |
+| `accountsReceivablePaid` | string | No | Accounts-receivable paid status for Transaction List |
+| `clearedStatus` | string | No | Cleared status filter for Transaction List |
+| `documentNumber` | string | No | Document number filter for Transaction List |
+| `sourceAccountType` | string | No | Source account type filter for Transaction List |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `reportType` | string | Financial report type that was run |
+| `header` | json | Native QuickBooks report header with name, periods, basis, currency, summarization, filters, and options |
+| ↳ `Time` | string | QuickBooks report generation timestamp |
+| ↳ `ReportName` | string | Native QuickBooks report name |
+| ↳ `DateMacro` | string | QuickBooks date macro, when returned |
+| ↳ `ReportBasis` | string | Cash or accrual basis |
+| ↳ `StartPeriod` | string | Report start date |
+| ↳ `EndPeriod` | string | Report end or as-of date |
+| ↳ `SummarizeColumnsBy` | string | Dimension or time period used for report columns |
+| ↳ `Currency` | string | Report currency |
+| ↳ `Customer` | string | Applied customer filter |
+| ↳ `Vendor` | string | Applied vendor filter |
+| ↳ `Account` | string | Applied account filter |
+| ↳ `Item` | string | Applied item filter |
+| ↳ `Class` | string | Applied class filter |
+| ↳ `Department` | string | Applied department filter |
+| ↳ `Option` | array | Native QuickBooks report options, including no-data indicators when present |
+| `columns` | json | Native QuickBooks report column definitions |
+| ↳ `Column` | array | Native report column definitions with titles, types, and metadata |
+| ↳ `ColTitle` | string | Column title |
+| ↳ `ColType` | string | QuickBooks column data type |
+| ↳ `MetaData` | array | Native column metadata name/value entries |
+| `rows` | json | Native hierarchical QuickBooks report rows and section summaries |
+| ↳ `Row` | array | Native hierarchical report rows; section rows may contain Header, nested Rows, and Summary, while data rows contain ColData values, IDs, and links |
+| ↳ `type` | string | QuickBooks row type |
+| ↳ `group` | string | QuickBooks section group |
+| ↳ `Header` | json | Section header column data |
+| ↳ `ColData` | array | Row values with optional operational IDs and links |
+| ↳ `Rows` | json | Nested native QuickBooks report rows |
+| ↳ `Summary` | json | Section summary column data |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Email Transaction
+
+Send a supported QuickBooks transaction by email. This causes an external email and Intuit limits sandbox email delivery.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionType` | string | Yes | Supported transaction type to email |
+| `transactionId` | string | Yes | QuickBooks transaction ID |
+| `recipient` | string | No | Required for Customer Payments; otherwise an optional single recipient override |
+| `confirmSend` | boolean | Yes | Explicit confirmation that an external email should be sent |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `transactionType` | string | Emailed QuickBooks transaction type |
+| `transactionId` | string | Emailed QuickBooks transaction ID |
+| `sent` | boolean | Whether QuickBooks accepted the email send request |
+| `record` | json | Native QuickBooks transaction returned after sending |
+| ↳ `Id` | string | QuickBooks transaction ID |
+| ↳ `SyncToken` | string | Current transaction sync token |
+| ↳ `DocNumber` | string | Transaction document number |
+| ↳ `TxnDate` | string | Transaction date |
+| ↳ `DueDate` | string | Transaction due date |
+| ↳ `ExpirationDate` | string | Estimate expiration date |
+| ↳ `CustomerRef` | json | Customer reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `CustomerMemo` | json | Customer-facing memo |
+| ↳ `DepositToAccountRef` | json | Deposit account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentMethodRef` | json | Payment method reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `PaymentRefNum` | string | Payment reference number |
+| ↳ `CurrencyRef` | json | Transaction currency reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `Line` | array | Native QuickBooks sales or purchasing transaction lines |
+| ↳ `Id` | string | QuickBooks transaction line ID |
+| ↳ `LineNum` | number | QuickBooks transaction line number |
+| ↳ `Description` | string | Transaction line description |
+| ↳ `Amount` | number | Transaction line amount |
+| ↳ `DetailType` | string | QuickBooks line detail type |
+| ↳ `LinkedTxn` | array | Transactions linked to this QuickBooks line |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `AccountBasedExpenseLineDetail` | json | Native QuickBooks account-based expense details |
+| ↳ `ItemBasedExpenseLineDetail` | json | Native QuickBooks item-based expense details |
+| ↳ `SalesItemLineDetail` | json | Native QuickBooks sales item line details |
+| ↳ `DescriptionLineDetail` | json | Native QuickBooks description line details |
+| ↳ `LinkedTxn` | array | Transactions linked by QuickBooks |
+| ↳ `TxnId` | string | Linked QuickBooks transaction ID |
+| ↳ `TxnType` | string | Linked QuickBooks transaction type |
+| ↳ `TxnLineId` | string | Linked QuickBooks transaction line ID |
+| ↳ `TotalAmt` | number | Transaction total amount |
+| ↳ `Balance` | number | Remaining transaction balance |
+| ↳ `UnappliedAmt` | number | Unapplied payment amount |
+| ↳ `PrivateNote` | string | Internal transaction note |
+| ↳ `TxnStatus` | string | Transaction status |
+| ↳ `TxnTaxDetail` | json | Calculated tax details |
+| ↳ `MetaData` | json | Transaction creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `VendorRef` | json | Vendor reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `APAccountRef` | json | Accounts-payable account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `AccountRef` | json | Payment account reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `EntityRef` | json | Purchase payee reference |
+| ↳ `value` | string | QuickBooks entity ID |
+| ↳ `name` | string | QuickBooks entity display name |
+| ↳ `type` | string | Referenced entity type |
+| ↳ `PaymentType` | string | Purchase payment type |
+| ↳ `PayType` | string | Bill-payment type |
+| ↳ `CheckPayment` | json | Check payment account details |
+| ↳ `CreditCardPayment` | json | Credit-card payment account details |
+| ↳ `POStatus` | string | Purchase order status |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Download Transaction PDF
+
+Download a supported QuickBooks transaction as a bounded PDF file
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `transactionType` | string | Yes | Supported transaction type to download |
+| `transactionId` | string | Yes | QuickBooks transaction ID |
+| `fileName` | string | No | Optional safe PDF filename override |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `file` | file | Downloaded file stored in execution files |
+| `fileName` | string | Safe downloaded filename |
+| `mimeType` | string | Downloaded file MIME type |
+| `size` | number | Downloaded file size in bytes |
+| `transactionType` | string | Downloaded QuickBooks transaction type |
+| `transactionId` | string | Downloaded QuickBooks transaction ID |
+
+### QuickBooks Read Attachments
+
+List attachment metadata for a fixed QuickBooks entity or read one attachment by ID
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `readMode` | string | Yes | Read mode: list or by_id |
+| `targetType` | string | No | Fixed QuickBooks entity type for List mode |
+| `targetId` | string | No | QuickBooks entity ID for List mode |
+| `attachmentId` | string | No | QuickBooks attachment ID for By ID mode |
+| `startPosition` | number | No | One-based list start position; defaults to 1 |
+| `maxResults` | number | No | List page size from 1 through 100; defaults to 25 |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `startPosition` | number | One-based position of the first item in this response |
+| `maxResults` | number | Actual number of items reported for this response |
+| `nextStartPosition` | number | Position to use when explicitly requesting the next page |
+| `hasMore` | boolean | Conservative indication that another page may exist |
+| `time` | string | QuickBooks response timestamp |
+| `item` | json | Native QuickBooks attachment metadata |
+| ↳ `Id` | string | QuickBooks attachment ID |
+| ↳ `SyncToken` | string | Attachment sync token |
+| ↳ `FileName` | string | Attached file name |
+| ↳ `ContentType` | string | Attached file MIME type |
+| ↳ `Size` | number | Attached file size in bytes |
+| ↳ `Note` | string | Attachment note or description |
+| ↳ `Category` | string | Native QuickBooks attachment category |
+| ↳ `AttachableRef` | array | QuickBooks entities referenced by this attachment |
+| ↳ `EntityRef` | json | Attached entity type and operational ID |
+| ↳ `IncludeOnSend` | boolean | Whether QuickBooks includes the attachment when sending |
+| ↳ `MetaData` | json | Attachment creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+| `items` | array | Native QuickBooks attachment metadata page |
+| ↳ `Id` | string | QuickBooks attachment ID |
+| ↳ `SyncToken` | string | Attachment sync token |
+| ↳ `FileName` | string | Attached file name |
+| ↳ `ContentType` | string | Attached file MIME type |
+| ↳ `Size` | number | Attached file size in bytes |
+| ↳ `Note` | string | Attachment note or description |
+| ↳ `Category` | string | Native QuickBooks attachment category |
+| ↳ `AttachableRef` | array | QuickBooks entities referenced by this attachment |
+| ↳ `EntityRef` | json | Attached entity type and operational ID |
+| ↳ `IncludeOnSend` | boolean | Whether QuickBooks includes the attachment when sending |
+| ↳ `MetaData` | json | Attachment creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+
+### QuickBooks Add Attachment
+
+Attach one supported file or one note to a fixed QuickBooks entity
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `attachmentKind` | string | Yes | Attachment kind: file or note |
+| `targetType` | string | Yes | Fixed QuickBooks entity type to attach to |
+| `targetId` | string | Yes | QuickBooks target entity ID |
+| `file` | file | No | Single Sim file to upload |
+| `fileName` | string | No | Optional safe filename override |
+| `contentType` | string | No | Optional compatible QuickBooks MIME type override |
+| `description` | string | No | Optional file attachment description |
+| `note` | string | No | Required nonempty note text in Note mode |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `attachment` | json | Created native QuickBooks attachment metadata |
+| ↳ `Id` | string | QuickBooks attachment ID |
+| ↳ `SyncToken` | string | Attachment sync token |
+| ↳ `FileName` | string | Attached file name |
+| ↳ `ContentType` | string | Attached file MIME type |
+| ↳ `Size` | number | Attached file size in bytes |
+| ↳ `Note` | string | Attachment note or description |
+| ↳ `Category` | string | Native QuickBooks attachment category |
+| ↳ `AttachableRef` | array | QuickBooks entities referenced by this attachment |
+| ↳ `EntityRef` | json | Attached entity type and operational ID |
+| ↳ `IncludeOnSend` | boolean | Whether QuickBooks includes the attachment when sending |
+| ↳ `MetaData` | json | Attachment creation and update timestamps |
+| ↳ `CreateTime` | string | Entity creation timestamp |
+| ↳ `LastUpdatedTime` | string | Entity last-updated timestamp |
+| ↳ `domain` | string | QuickBooks domain |
+| ↳ `sparse` | boolean | Whether this is a sparse entity |
+| `attachmentId` | string | Created QuickBooks attachment ID |
+| `attachmentKind` | string | Created attachment kind |
+| `targetType` | string | QuickBooks target entity type |
+| `targetId` | string | QuickBooks target entity ID |
+| `time` | string | QuickBooks response timestamp |
+
+### QuickBooks Download Attachment
+
+Download a QuickBooks file attachment as a stored Sim file
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `attachmentId` | string | Yes | QuickBooks attachment ID |
+| `fileName` | string | No | Optional safe filename override |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `file` | file | Downloaded file stored in execution files |
+| `fileName` | string | Safe downloaded filename |
+| `mimeType` | string | Downloaded file MIME type |
+| `size` | number | Downloaded file size in bytes |
+| `attachmentId` | string | Downloaded QuickBooks attachment ID |
+
+
+
+## Triggers
+
+A **Trigger** is a block that starts a workflow when an event happens in this service.
+
+### QuickBooks Account Events
+
+Trigger when selected Account events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_account_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Bill Events
+
+Trigger when selected Bill events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_bill_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Bill Payment Events
+
+Trigger when selected Bill Payment events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_bill_payment_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Budget Events
+
+Trigger when selected Budget events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_budget_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Class Events
+
+Trigger when selected Class events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_class_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Credit Memo Events
+
+Trigger when selected Credit Memo events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_credit_memo_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Currency Events
+
+Trigger when selected Currency events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_currency_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Customer Events
+
+Trigger when selected Customer events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_customer_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Department Events
+
+Trigger when selected Department events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_department_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Deposit Events
+
+Trigger when selected Deposit events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_deposit_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Employee Events
+
+Trigger when selected Employee events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_employee_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Estimate Events
+
+Trigger when selected Estimate events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_estimate_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Invoice Events
+
+Trigger when selected Invoice events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_invoice_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Item Events
+
+Trigger when selected Item events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_item_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Journal Code Events
+
+Trigger when selected Journal Code events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_journal_code_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Journal Entry Events
+
+Trigger when selected Journal Entry events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_journal_entry_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Payment Events
+
+Trigger when selected Payment events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_payment_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Payment Method Events
+
+Trigger when selected Payment Method events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_payment_method_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Preferences Updated
+
+Trigger when QuickBooks Preferences are updated
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Purchase Events
+
+Trigger when selected Purchase events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_purchase_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Purchase Order Events
+
+Trigger when selected Purchase Order events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_purchase_order_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Refund Receipt Events
+
+Trigger when selected Refund Receipt events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_refund_receipt_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Sales Receipt Events
+
+Trigger when selected Sales Receipt events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_sales_receipt_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Tax Agency Events
+
+Trigger when selected Tax Agency events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_tax_agency_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Term Events
+
+Trigger when selected Term events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_term_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Time Activity Events
+
+Trigger when selected Time Activity events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_time_activity_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Transfer Events
+
+Trigger when selected Transfer events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_transfer_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Vendor Credit Events
+
+Trigger when selected Vendor Credit events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_vendor_credit_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
+
+---
+
+### QuickBooks Vendor Events
+
+Trigger when selected Vendor events occur in QuickBooks
+
+#### Configuration
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `triggerCredentials` | string | Yes | QuickBooks Account |
+| `eventTypes_quickbooks_vendor_events` | string | Yes | Event Types |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `eventId` | string | Intuit webhook event ID |
+| `eventType` | string | Full Intuit CloudEvent type |
+| `entityType` | string | QuickBooks entity type |
+| `action` | string | QuickBooks webhook action |
+| `entityId` | string | QuickBooks entity ID |
+| `realmId` | string | QuickBooks company realm ID |
+| `eventTime` | string | Event timestamp |
+| `specVersion` | string | CloudEvents specification version |
+| `source` | string | Intuit event source |
+| `contentType` | string | Event content type, when provided |
+| `data` | json | Optional event data supplied by Intuit |
+
diff --git a/apps/docs/content/docs/integrations/sqs.mdx b/apps/docs/content/docs/integrations/sqs.mdx
index aff6b8179e0..17c9c60c038 100644
--- a/apps/docs/content/docs/integrations/sqs.mdx
+++ b/apps/docs/content/docs/integrations/sqs.mdx
@@ -21,17 +21,20 @@ With Amazon SQS, you can:
- **Ensure reliability**: Built-in redundancy and high availability
- **Support FIFO queues**: Maintain strict message ordering and exactly-once processing
-In Sim, the SQS integration enables your agents to send messages to Amazon SQS queues securely and programmatically. Supported operations include:
+In Sim, the SQS integration gives your agents both sides of the queue — producing work and consuming it. Supported operations cover:
-- **Send Message**: Send messages to SQS queues with optional message group ID and deduplication ID for FIFO queues
+- **Messages**: Send one message or a batch of up to 10, receive with long polling, delete individually or in batches, and extend visibility timeouts while work is still in flight
+- **Queues**: Create, delete, and purge queues, look up a queue URL by name, and read or update queue attributes
+- **Dead-letter handling**: List the source queues feeding a dead-letter queue, then start, monitor, and cancel message move tasks to redrive failed messages back
+- **Tags**: List, add, and remove queue tags for cost allocation and ownership tracking
-This integration allows your agents to automate message sending workflows without manual intervention. By connecting Sim with Amazon SQS, you can build agents that publish messages to queues within your workflows—all without handling queue infrastructure or connections.
+Because an agent can now drain a queue rather than only fill it, SQS becomes a way to hand work to Sim as well as from it. A workflow can long-poll a queue for jobs, process each message, delete it on success, and let the visibility timeout return anything it fails to finish — the standard reliable-consumer pattern, without running a worker of your own.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
-Integrate Amazon SQS into the workflow. Can send messages to SQS queues.
+Integrate Amazon SQS into the workflow. Send and receive messages one at a time or in batches of ten, delete messages, extend visibility timeouts, manage queues along with their attributes and tags, and redrive messages out of a dead-letter queue.
@@ -49,7 +52,9 @@ Send a message to an Amazon SQS queue
| `accessKeyId` | string | Yes | AWS access key ID |
| `secretAccessKey` | string | Yes | AWS secret access key |
| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
-| `data` | object | Yes | Message body to send as JSON object \(e.g., \{ "action": "process", "payload": \{...\} \}\) |
+| `data` | json | Yes | Message body to send as JSON object \(e.g., \{ "action": "process", "payload": \{...\} \}\) |
+| `delaySeconds` | number | No | Seconds to delay delivery of this message, 0-900. Not supported per-message on FIFO queues |
+| `messageAttributes` | json | No | Message attributes keyed by name, each \{ "dataType": "String" \| "Number", "stringValue": "..." \}. A custom label such as Number.float is allowed; Binary attributes are not supported |
| `messageGroupId` | string | No | Message group ID for FIFO queues \(e.g., "order-processing-group"\) |
| `messageDeduplicationId` | string | No | Message deduplication ID for FIFO queues \(e.g., "order-12345-v1"\) |
@@ -59,5 +64,470 @@ Send a message to an Amazon SQS queue
| --------- | ---- | ----------- |
| `message` | string | Operation status message |
| `id` | string | Message ID |
+| `md5OfMessageBody` | string | MD5 digest of the message body, for verifying SQS received it intact |
+| `md5OfMessageAttributes` | string | MD5 digest of the message attributes |
+| `sequenceNumber` | string | Large, non-consecutive sequence number assigned by a FIFO queue |
+
+### SQS Send Message Batch
+
+Send up to 10 messages to an Amazon SQS queue in a single request
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `entries` | array | Yes | Up to 10 entries, each \{ "id": "unique-id", "data": \{ ... \}, "delaySeconds"?, "messageGroupId"?, "messageDeduplicationId"?, "messageAttributes"? \} |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `successful` | array | Entries that were accepted |
+| ↳ `id` | string | Id supplied for this batch entry |
+| ↳ `messageId` | string | Message ID assigned by SQS |
+| ↳ `md5OfMessageBody` | string | MD5 digest of the message body |
+| ↳ `md5OfMessageAttributes` | string | MD5 digest of the message attributes |
+| ↳ `sequenceNumber` | string | Sequence number assigned by a FIFO queue |
+| `failed` | array | Entries that were rejected |
+| ↳ `id` | string | Id supplied for this batch entry |
+| ↳ `senderFault` | boolean | Whether the sender caused the failure |
+| ↳ `code` | string | Error code for the failure |
+| ↳ `message` | string | Human-readable failure message |
+| `successCount` | number | Number of messages accepted |
+| `failureCount` | number | Number of messages rejected |
+
+### SQS Receive Message
+
+Receive up to 10 messages from an Amazon SQS queue, with optional long polling
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `maxNumberOfMessages` | number | No | Maximum number of messages to return, 1-10 \(default 1\) |
+| `waitTimeSeconds` | number | No | Long-poll duration in seconds, 0-20. Waits for a message to arrive before returning \(default 0, short poll\) |
+| `visibilityTimeout` | number | No | Seconds the returned messages stay hidden from other consumers, 0-43200. Defaults to the queue setting |
+| `messageAttributeNames` | array | No | Names of user-defined message attributes to return. Use \["All"\] to return all of them |
+| `messageSystemAttributeNames` | array | No | System attributes to return: All, SenderId, SentTimestamp, ApproximateReceiveCount, ApproximateFirstReceiveTimestamp, SequenceNumber, MessageDeduplicationId, MessageGroupId, AWSTraceHeader, DeadLetterQueueSourceArn |
+| `receiveRequestAttemptId` | string | No | FIFO queues only: deduplication token that lets a retried receive return the same messages \(max 128 characters\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `messages` | array | Received messages. Pass a receiptHandle to sqs_delete_message, sqs_delete_message_batch, sqs_change_message_visibility, or sqs_change_message_visibility_batch |
+| ↳ `messageId` | string | Unique ID SQS assigned to the message |
+| ↳ `receiptHandle` | string | Handle identifying this receipt of the message, required to delete it |
+| ↳ `body` | string | Message body as it was sent |
+| ↳ `md5OfBody` | string | MD5 digest of the message body |
+| ↳ `md5OfMessageAttributes` | string | MD5 digest of the message attributes |
+| ↳ `attributes` | json | Requested system attributes as string values keyed by attribute name |
+| ↳ `messageAttributes` | json | Requested user-defined attributes, each with dataType, stringValue, and stringListValues |
+| `count` | number | Number of messages returned |
+
+### SQS Delete Message
+
+Delete a received message from an Amazon SQS queue using its receipt handle
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `receiptHandle` | string | Yes | Receipt handle returned by sqs_receive_message for the message to delete |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Delete Message Batch
+
+Delete up to 10 received messages from an Amazon SQS queue in a single request
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `entries` | array | Yes | Up to 10 entries, each \{ "id": "unique-id", "receiptHandle": "..." \}. Receipt handles come from sqs_receive_message |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `successful` | array | Entries that were deleted |
+| ↳ `id` | string | Id supplied for this batch entry |
+| `failed` | array | Entries that were rejected |
+| ↳ `id` | string | Id supplied for this batch entry |
+| ↳ `senderFault` | boolean | Whether the sender caused the failure |
+| ↳ `code` | string | Error code for the failure |
+| ↳ `message` | string | Human-readable failure message |
+| `successCount` | number | Number of messages deleted |
+| `failureCount` | number | Number of messages rejected |
+
+### SQS Change Message Visibility
+
+Change how long a received Amazon SQS message stays hidden from other consumers
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `receiptHandle` | string | Yes | Receipt handle returned by sqs_receive_message for the message to update |
+| `visibilityTimeout` | number | Yes | New visibility timeout in seconds, 0-43200 \(12 hours\). 0 makes the message immediately visible again |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Change Message Visibility Batch
+
+Change the visibility timeout of up to 10 received Amazon SQS messages at once
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `entries` | array | Yes | Up to 10 entries, each \{ "id": "unique-id", "receiptHandle": "...", "visibilityTimeout": 0-43200 \}. Receipt handles come from sqs_receive_message |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `successful` | array | Entries that were updated |
+| ↳ `id` | string | Id supplied for this batch entry |
+| `failed` | array | Entries that were rejected |
+| ↳ `id` | string | Id supplied for this batch entry |
+| ↳ `senderFault` | boolean | Whether the sender caused the failure |
+| ↳ `code` | string | Error code for the failure |
+| ↳ `message` | string | Human-readable failure message |
+| `successCount` | number | Number of messages updated |
+| `failureCount` | number | Number of messages rejected |
+
+### SQS List Queues
+
+List Amazon SQS queue URLs in a region, optionally filtered by name prefix
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueNamePrefix` | string | No | Return only queues whose name begins with this string \(case-sensitive\) |
+| `maxResults` | number | No | Maximum queues to return, 1-1000. Must be set to receive a nextToken \(default returns up to 1000\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queueUrls` | array | Queue URLs returned by the request |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of queue URLs returned |
+
+### SQS Get Queue URL
+
+Resolve an Amazon SQS queue name to its queue URL
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueName` | string | Yes | Queue name, up to 80 characters of letters, digits, hyphens and underscores. A FIFO queue name ends in .fifo |
+| `queueOwnerAwsAccountId` | string | No | 12-digit AWS account ID of the queue owner, when the queue belongs to another account |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queueUrl` | string | URL of the queue |
+
+### SQS Get Queue Attributes
+
+Read configuration and message-count attributes of an Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `attributeNames` | array | No | Attributes to return, e.g. \["All"\], \["ApproximateNumberOfMessages"\], \["QueueArn"\], \["VisibilityTimeout"\], \["RedrivePolicy"\]. Omitting this returns no attributes |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `attributes` | json | Queue attributes as string values keyed by attribute name \(e.g., ApproximateNumberOfMessages, QueueArn, VisibilityTimeout, RedrivePolicy\) |
+
+### SQS Set Queue Attributes
+
+Update configuration attributes of an existing Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `attributes` | json | Yes | Attributes to set as string values, e.g. \{ "VisibilityTimeout": "60", "MessageRetentionPeriod": "345600", "RedrivePolicy": "\{...\}" \}. FifoQueue can only be set at creation |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Create Queue
+
+Create a standard or FIFO Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueName` | string | Yes | Queue name, up to 80 characters of letters, digits, hyphens and underscores. A FIFO queue name must end in .fifo |
+| `attributes` | json | No | Queue attributes as string values, e.g. \{ "FifoQueue": "true", "VisibilityTimeout": "30", "DelaySeconds": "0", "RedrivePolicy": "\{...\}" \} |
+| `tags` | json | No | Cost-allocation tags to apply to the new queue, as \{ "key": "value" \} pairs |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `queueUrl` | string | URL of the created queue |
+
+### SQS Delete Queue
+
+Delete an Amazon SQS queue and every message still in it
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Purge Queue
+
+Delete every message in an Amazon SQS queue while keeping the queue itself
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS List Dead-Letter Source Queues
+
+List the Amazon SQS queues that use a given queue as their dead-letter queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | URL of the dead-letter queue whose source queues should be listed |
+| `maxResults` | number | No | Maximum source queues to return, 1-1000. Must be set to receive a nextToken \(default returns up to 1000\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `queueUrls` | array | URLs of the source queues that redrive to this dead-letter queue |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of source queues returned |
+
+### SQS List Queue Tags
+
+List the cost-allocation tags attached to an Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `tags` | json | Tags attached to the queue, as string values keyed by tag key |
+
+### SQS Tag Queue
+
+Add or overwrite cost-allocation tags on an Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `tags` | json | Yes | Tags to apply as \{ "key": "value" \} pairs. An existing key is overwritten. AWS recommends no more than 50 tags per queue |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Untag Queue
+
+Remove cost-allocation tags from an Amazon SQS queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `queueUrl` | string | Yes | SQS queue URL \(e.g., https://sqs.us-east-1.amazonaws.com/123456789012/my-queue\) |
+| `tagKeys` | array | Yes | Tag keys to remove, e.g. \["env", "team"\] |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+
+### SQS Start Message Move Task
+
+Start redriving messages out of an Amazon SQS dead-letter queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `sourceArn` | string | Yes | ARN of the dead-letter queue to move messages out of \(e.g., arn:aws:sqs:us-east-1:123456789012:my-dlq\) |
+| `destinationArn` | string | No | ARN of the queue to move messages into. Omit to redrive each message to its original source queue |
+| `maxNumberOfMessagesPerSecond` | number | No | Throttle the move to at most this many messages per second, up to 500. Omit to move as fast as possible |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `taskHandle` | string | Handle identifying the move task, accepted by sqs_cancel_message_move_task |
+
+### SQS List Message Move Tasks
+
+List the most recent message move tasks for an Amazon SQS source queue
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `sourceArn` | string | Yes | ARN of the queue whose move tasks should be listed \(e.g., arn:aws:sqs:us-east-1:123456789012:my-dlq\) |
+| `maxResults` | number | No | Maximum move tasks to return, 1-10 \(default 1\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `results` | array | Move tasks for the source queue |
+| ↳ `taskHandle` | string | Handle of the task, populated only while its status is RUNNING |
+| ↳ `status` | string | RUNNING, COMPLETED, CANCELLING, CANCELLED, or FAILED |
+| ↳ `sourceArn` | string | ARN of the source queue |
+| ↳ `destinationArn` | string | ARN of the destination queue, absent when redriving to source queues |
+| ↳ `maxNumberOfMessagesPerSecond` | number | Per-second throttle applied to the move |
+| ↳ `approximateNumberOfMessagesMoved` | number | Approximate number of messages moved so far |
+| ↳ `approximateNumberOfMessagesToMove` | number | Approximate number of messages still to move |
+| ↳ `failureReason` | string | Why the task failed, set only when the status is FAILED |
+| ↳ `startedTimestamp` | number | Epoch milliseconds when the task started |
+| `count` | number | Number of move tasks returned |
+
+### SQS Cancel Message Move Task
+
+Cancel an in-progress Amazon SQS message move task
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `taskHandle` | string | Yes | Task handle returned by sqs_start_message_move_task or sqs_list_message_move_tasks. Only a RUNNING task can be cancelled |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `approximateNumberOfMessagesMoved` | number | Approximate number of messages already moved before the task was cancelled |
diff --git a/apps/docs/content/docs/integrations/ssm.mdx b/apps/docs/content/docs/integrations/ssm.mdx
new file mode 100644
index 00000000000..a2600c5bec3
--- /dev/null
+++ b/apps/docs/content/docs/integrations/ssm.mdx
@@ -0,0 +1,636 @@
+---
+title: AWS Systems Manager
+description: Run commands, manage parameters, and audit managed nodes
+---
+
+import { BlockInfoCard } from "@/components/ui/block-info-card"
+
+
+
+{/* MANUAL-CONTENT-START:intro */}
+[AWS Systems Manager](https://aws.amazon.com/systems-manager/) is the operations hub for AWS. It gives you a single place to run commands across fleets of managed nodes, store configuration and secrets, track patch and compliance state, and execute runbooks — without opening SSH, managing bastion hosts, or distributing long-lived credentials.
+
+With AWS Systems Manager, you can:
+
+- **Run commands remotely**: Execute shell or PowerShell across a fleet by instance ID or tag-based targets, with concurrency and error thresholds you control
+- **Store configuration and secrets**: Keep parameters in Parameter Store as plain strings, string lists, or KMS-encrypted SecureStrings
+- **Inspect your fleet**: List managed nodes with their platform, agent version, and last ping time
+- **Track patch state**: Read per-instance patch installations and summary compliance counts
+- **Audit compliance**: Query compliance items and summaries across your managed nodes
+- **Automate runbooks**: Start, monitor, and stop Automation executions built on SSM documents
+
+In Sim, the Systems Manager integration is what lets an agent act on infrastructure rather than only report on it. Paired with CloudWatch or CloudTrail for detection, a workflow can investigate an alert, run a diagnostic command against the affected nodes, read the configuration behind the failure from Parameter Store, and kick off an Automation runbook to remediate — end to end, with every step logged in your run history.
+
+Parameter Store decryption is opt-in: `Get Parameter`, `Get Parameters`, and `Get Parameters By Path` leave `WithDecryption` off unless you explicitly enable it, so a SecureString stays encrypted by default.
+
+Be precise about what that protects, because it is narrower than it looks. The value you supply to `Put Parameter` is masked in the editor and is never echoed back in the operation's result, and Sim never puts a parameter value into an error message. It is **not** kept out of the run log: block inputs are recorded, and a value typed directly into the field is recorded verbatim. Referencing an environment variable instead — `{{MY_SECRET}}` — keeps the literal out of the log, because references are restored to their placeholder before the log is written.
+
+Reads are exposed the same way. Once you enable decryption the plaintext is ordinary block output: it flows to downstream blocks as intended, and it is written to the run log and the execution trace like any other output.
+
+So enable decryption only on the steps that genuinely need the plaintext, prefer environment-variable references over typed literals when writing, and treat the run logs of any workflow that touches SecureString values as secret material.
+{/* MANUAL-CONTENT-END */}
+
+
+## Usage Instructions
+
+Integrate AWS Systems Manager into your workflow. Run commands on managed nodes, read and write Parameter Store values, inspect node inventory and patch compliance, and drive Automation runbooks.
+
+
+
+## Actions
+
+### SSM Send Command
+
+Run an SSM document on managed nodes with AWS Systems Manager Run Command
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `documentName` | string | Yes | Name of the SSM document to run \(e.g., AWS-RunShellScript\) |
+| `instanceIds` | json | No | Managed node IDs to target, as an array of strings \(e.g., \["i-0123456789abcdef0"\]\). Provide instanceIds or targets |
+| `targets` | json | No | Tag or resource-group targets, as an array of \{Key, Values\} objects. Provide instanceIds or targets |
+| `documentVersion` | string | No | Document version to run \($LATEST, $DEFAULT, or a version number\) |
+| `parameters` | json | No | Document parameters, as an object mapping each parameter name to an array of string values |
+| `comment` | string | No | Comment describing the command, at most 100 characters |
+| `executionTimeoutSeconds` | number | No | Seconds to wait for a node to acknowledge the command before it times out \(30-2592000\) |
+| `maxConcurrency` | string | No | Number or percentage of nodes to run the command on at once \(e.g., 10 or 50%\) |
+| `maxErrors` | string | No | Number or percentage of errors allowed before the command stops \(e.g., 0 or 10%\) |
+| `outputS3BucketName` | string | No | S3 bucket to store command output in |
+| `outputS3KeyPrefix` | string | No | S3 key prefix for stored command output |
+| `serviceRoleArn` | string | No | ARN of the IAM service role Systems Manager uses to publish notifications |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `commandId` | string | ID of the command; pass it to ssm_get_command_invocation or ssm_list_command_invocations to read per-node results |
+| `documentName` | string | Name of the document that was run |
+| `documentVersion` | string | Document version that was run |
+| `comment` | string | Comment supplied with the command |
+| `status` | string | Command status \(Pending, InProgress, Success, Cancelled, Failed, TimedOut, Cancelling\) |
+| `statusDetails` | string | Detailed status of the command |
+| `requestedDateTime` | string | When the command was requested |
+| `expiresAfter` | string | When the command stops being dispatched to nodes that have not run it |
+| `instanceIds` | array | Managed node IDs the command targets |
+| `targets` | json | Tag or resource-group targets the command was sent to, as an array of \{key, values\} |
+| `maxConcurrency` | string | Concurrency setting the command ran with |
+| `maxErrors` | string | Error threshold the command ran with |
+| `targetCount` | number | Number of targets the command was sent to |
+| `completedCount` | number | Number of targets that have completed the command |
+| `errorCount` | number | Number of targets whose command execution failed |
+| `deliveryTimedOutCount` | number | Number of targets the command could not be delivered to in time |
+| `executionTimeoutSeconds` | number | Acknowledgement timeout the command ran with |
+| `outputS3BucketName` | string | S3 bucket command output is written to |
+| `outputS3KeyPrefix` | string | S3 key prefix command output is written under |
+| `outputS3Region` | string | S3 region reported for command output |
+| `serviceRole` | string | IAM service role used for notifications |
+
+### SSM List Commands
+
+List Run Command executions in an AWS account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `commandId` | string | No | Return only the command with this ID |
+| `instanceId` | string | No | Return only commands sent to this managed node |
+| `filters` | json | No | Filters, as an array of \{key, value\} objects. Valid keys: InvokedAfter, InvokedBefore, Status, ExecutionStage, DocumentName |
+| `maxResults` | number | No | Maximum number of commands to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `commands` | json | Commands, each with commandId, documentName, status, statusDetails, requestedDateTime, instanceIds, targets, targetCount, completedCount, and errorCount |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of commands returned |
+
+### SSM List Command Invocations
+
+List the per-node invocations of Run Command executions
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `commandId` | string | No | Return only invocations of this command |
+| `instanceId` | string | No | Return only invocations on this managed node |
+| `filters` | json | No | Filters, as an array of \{key, value\} objects. Valid keys: InvokedAfter, InvokedBefore, Status, DocumentName |
+| `details` | boolean | No | Include per-plugin detail \(command plugins and their output\) for each invocation |
+| `maxResults` | number | No | Maximum number of invocations to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `commandInvocations` | json | Invocations, each with commandId, instanceId, instanceName, status, statusDetails, requestedDateTime, standardOutputUrl, standardErrorUrl, and commandPlugins |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of invocations returned |
+
+### SSM Get Command Invocation
+
+Read the output and status of a Run Command execution on one managed node
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `commandId` | string | Yes | ID of the command, as returned by ssm_send_command |
+| `instanceId` | string | Yes | Managed node the command ran on \(e.g., i-0123456789abcdef0\) |
+| `pluginName` | string | No | Name of the document plugin to read output for; required for documents with more than one plugin |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `commandId` | string | ID of the command |
+| `instanceId` | string | Managed node the command ran on |
+| `comment` | string | Comment supplied with the command |
+| `documentName` | string | Document that was run |
+| `documentVersion` | string | Document version that was run |
+| `pluginName` | string | Plugin the output belongs to |
+| `responseCode` | number | Exit code of the command, or -1 if it has not started |
+| `executionStartDateTime` | string | When the command started running on the node |
+| `executionElapsedTime` | string | How long the command ran, as an ISO 8601 duration |
+| `executionEndDateTime` | string | When the command finished running on the node |
+| `status` | string | Invocation status \(Pending, InProgress, Delayed, Success, Cancelled, TimedOut, Failed, Cancelling\) |
+| `statusDetails` | string | Detailed status of the invocation |
+| `standardOutputContent` | string | First 24000 characters of stdout; longer output is available at standardOutputUrl |
+| `standardOutputUrl` | string | S3 URL of the full stdout, if S3 output was configured |
+| `standardErrorContent` | string | First 8000 characters of stderr; longer output is available at standardErrorUrl |
+| `standardErrorUrl` | string | S3 URL of the full stderr, if S3 output was configured |
+
+### SSM Cancel Command
+
+Cancel an in-flight Run Command execution
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `commandId` | string | Yes | ID of the command to cancel, as returned by ssm_send_command |
+| `instanceIds` | array | No | Managed node IDs to cancel on \(e.g., \["i-0123456789abcdef0"\]\); omit to cancel on every targeted node |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `commandId` | string | ID of the command that was cancelled |
+
+### SSM Get Parameter
+
+Read one parameter from AWS Systems Manager Parameter Store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Name of the parameter, optionally with a :version or :label suffix |
+| `withDecryption` | boolean | No | Return the decrypted value of a SecureString parameter; ignored for String and StringList parameters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `name` | string | Name of the parameter |
+| `type` | string | Parameter type \(String, StringList, or SecureString\) |
+| `value` | string | Parameter value; encrypted unless withDecryption was set for a SecureString |
+| `version` | number | Version of the parameter |
+| `selector` | string | Version or label selector used to read the parameter |
+| `sourceResult` | string | Raw result from the source for a parameter served by another service |
+| `lastModifiedDate` | string | When the parameter was last changed |
+| `arn` | string | ARN of the parameter |
+| `dataType` | string | Data type of the parameter \(text, aws:ec2:image, or aws:ssm:integration\) |
+
+### SSM Get Parameters
+
+Read up to ten parameters from AWS Systems Manager Parameter Store by name
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `names` | json | Yes | Parameter names to read, as an array of at most 10 strings |
+| `withDecryption` | boolean | No | Return decrypted values for SecureString parameters; ignored for String and StringList parameters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `parameters` | json | Parameters that were found, each with name, type, value, version, arn, dataType, and lastModifiedDate |
+| `invalidParameters` | array | Names that could not be read because they do not exist or are malformed |
+| `count` | number | Number of parameters returned |
+
+### SSM Get Parameters By Path
+
+Read parameters under a Parameter Store hierarchy path
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `path` | string | Yes | Hierarchy path to read, starting with a slash \(e.g., /prod/app\) |
+| `recursive` | boolean | No | Include parameters in nested paths below the given path |
+| `withDecryption` | boolean | No | Return decrypted values for SecureString parameters; ignored for String and StringList parameters |
+| `parameterFilters` | json | No | Filters, as an array of \{Key, Option, Values\} objects. Valid keys here: Type, KeyId, Label |
+| `maxResults` | number | No | Maximum number of parameters to return \(1-10\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `parameters` | json | Parameters under the path, each with name, type, value, version, arn, dataType, and lastModifiedDate |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of parameters returned |
+
+### SSM Put Parameter
+
+Create or update a parameter in AWS Systems Manager Parameter Store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Name of the parameter, optionally using a slash-separated hierarchy |
+| `value` | string | Yes | Value to store |
+| `type` | string | No | Parameter type \(String, StringList, or SecureString\); required when creating a new parameter |
+| `description` | string | No | Description of the parameter |
+| `keyId` | string | No | KMS key ID or ARN used to encrypt a SecureString parameter; defaults to the account key |
+| `overwrite` | boolean | No | Overwrite the parameter if it already exists |
+| `allowedPattern` | string | No | Regular expression the value must match |
+| `tier` | string | No | Parameter tier \(Standard, Advanced, or Intelligent-Tiering\) |
+| `dataType` | string | No | Data type of the parameter \(text, aws:ec2:image, or aws:ssm:integration\) |
+| `policies` | string | No | Parameter policies as a JSON array string; Advanced tier only |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `name` | string | Name of the parameter that was written |
+| `version` | number | Version number the write produced |
+| `tier` | string | Tier the parameter was stored in |
+
+### SSM Delete Parameter
+
+Delete a parameter from AWS Systems Manager Parameter Store
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Name of the parameter to delete |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `name` | string | Name of the parameter that was deleted |
+
+### SSM Describe Parameters
+
+List Parameter Store parameter metadata without reading any values
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `parameterFilters` | json | No | Filters, as an array of \{Key, Option, Values\} objects. Valid keys: Name, Type, KeyId, Path, Tier, DataType, or tag:<key> |
+| `shared` | boolean | No | Return parameters shared with this account instead of parameters it owns |
+| `maxResults` | number | No | Maximum number of parameters to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `parameters` | json | Parameter metadata, each with name, arn, type, keyId, description, tier, version, dataType, allowedPattern, lastModifiedDate, lastModifiedUser, and policies. Values are never included |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of parameters returned |
+
+### SSM Describe Instance Information
+
+List managed nodes registered with AWS Systems Manager and their agent status
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `filters` | json | No | Filters, as an array of \{Key, Values\} objects. Valid keys: InstanceIds, AgentVersion, PingStatus, PlatformTypes, ActivationIds, IamRole, ResourceType, AssociationStatus, SourceIds, SourceTypes, tag-key, or tag:<key> |
+| `maxResults` | number | No | Maximum number of nodes to return \(5-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `instances` | json | Managed nodes, each with instanceId, pingStatus, lastPingDateTime, agentVersion, isLatestVersion, platformType, platformName, platformVersion, computerName, ipAddress, iamRole, resourceType, and associationStatus |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of managed nodes returned |
+
+### SSM Describe Instance Patches
+
+List the patches reported for one managed node
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `instanceId` | string | Yes | Managed node to report patches for \(e.g., i-0123456789abcdef0\) |
+| `filters` | json | No | Filters, as an array of \{Key, Values\} objects. Valid keys: Classification, KBId, Severity, State |
+| `maxResults` | number | No | Maximum number of patches to return \(10-100\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `patches` | json | Patches, each with title, kbId, classification, severity, state, installedTime, and cveIds |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of patches returned |
+
+### SSM Describe Instance Patch States
+
+Read patch compliance summaries for a set of managed nodes
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `instanceIds` | json | Yes | Managed node IDs to summarize, as an array of at most 50 strings |
+| `maxResults` | number | No | Maximum number of patch states to return \(10-100\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `instancePatchStates` | json | Patch states, each with instanceId, patchGroup, baselineId, operation, operationStartTime, operationEndTime, installedCount, missingCount, failedCount, notApplicableCount, criticalNonCompliantCount, and securityNonCompliantCount |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of patch states returned |
+
+### SSM List Compliance Items
+
+List individual compliance findings reported to AWS Systems Manager
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `resourceIds` | json | No | Resource to report on, as an array holding a single managed node ID |
+| `resourceTypes` | json | No | Resource type to report on, as an array holding a single value; currently only ManagedInstance is supported |
+| `filters` | json | No | Filters, as an array of \{Key, Values, Type\} objects. Type is one of EQUAL, NOT_EQUAL, BEGIN_WITH, LESS_THAN, GREATER_THAN |
+| `maxResults` | number | No | Maximum number of compliance items to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `complianceItems` | json | Compliance items, each with complianceType, resourceType, resourceId, id, title, status, severity, executionTime, executionId, executionType, and details |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of compliance items returned |
+
+### SSM List Compliance Summaries
+
+Read compliant and non-compliant counts per compliance type
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `filters` | json | No | Filters, as an array of \{Key, Values, Type\} objects. Type is one of EQUAL, NOT_EQUAL, BEGIN_WITH, LESS_THAN, GREATER_THAN |
+| `maxResults` | number | No | Maximum number of summaries to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `complianceSummaryItems` | json | Summaries, each with complianceType, compliantCount, compliantSeveritySummary, nonCompliantCount, and nonCompliantSeveritySummary |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of summaries returned |
+
+### SSM Start Automation Execution
+
+Start an AWS Systems Manager Automation runbook execution
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `documentName` | string | Yes | Name of the Automation runbook to run \(e.g., AWS-RestartEC2Instance\) |
+| `documentVersion` | string | No | Runbook version to run \($LATEST, $DEFAULT, or a version number\) |
+| `parameters` | json | No | Runbook parameters, as an object mapping each parameter name to an array of string values |
+| `mode` | string | No | Execution mode, Auto or Interactive |
+| `targetParameterName` | string | No | Runbook parameter that receives each resolved target; required when targets is set |
+| `targets` | json | No | Rate-control target, as an array holding a single \{Key, Values\} object; requires targetParameterName |
+| `maxConcurrency` | string | No | Number or percentage of targets to run against at once \(e.g., 10 or 50%\) |
+| `maxErrors` | string | No | Number or percentage of errors allowed before the execution stops \(e.g., 0 or 10%\) |
+| `clientToken` | string | No | Idempotency token, exactly 36 characters |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `automationExecutionId` | string | ID of the execution; pass it to ssm_get_automation_execution or ssm_stop_automation_execution |
+
+### SSM Describe Automation Executions
+
+List Automation runbook executions in an AWS account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `filters` | json | No | Filters, as an array of \{Key, Values\} objects. Valid keys: DocumentNamePrefix, ExecutionStatus, ExecutionId, ParentExecutionId, CurrentAction, StartTimeBefore, StartTimeAfter, AutomationType, TagKey, TargetResourceGroup, AutomationSubtype, OpsItemId |
+| `maxResults` | number | No | Maximum number of executions to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `automationExecutions` | json | Executions, each with automationExecutionId, documentName, documentVersion, automationExecutionStatus, executionStartTime, executionEndTime, executedBy, currentStepName, currentAction, failureMessage, and outputs |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of executions returned |
+
+### SSM Get Automation Execution
+
+Read the status, outputs, and step results of one Automation execution
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `automationExecutionId` | string | Yes | ID of the execution, as returned by ssm_start_automation_execution |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `automationExecutionId` | string | ID of the execution |
+| `documentName` | string | Runbook that was run |
+| `documentVersion` | string | Runbook version that was run |
+| `automationExecutionStatus` | string | Execution status \(Pending, InProgress, Waiting, Success, TimedOut, Cancelling, Cancelled, Failed, and related values\) |
+| `executionStartTime` | string | When the execution started |
+| `executionEndTime` | string | When the execution finished |
+| `executedBy` | string | IAM identity that started the execution |
+| `mode` | string | Execution mode, Auto or Interactive |
+| `parentAutomationExecutionId` | string | ID of the parent execution, for a child execution |
+| `currentStepName` | string | Step the execution is currently running |
+| `currentAction` | string | Action the execution is currently running |
+| `failureMessage` | string | Reason the execution failed |
+| `targetParameterName` | string | Runbook parameter that received each resolved target |
+| `target` | string | Resource the execution targeted |
+| `maxConcurrency` | string | Concurrency setting the execution ran with |
+| `maxErrors` | string | Error threshold the execution ran with |
+| `parameters` | json | Parameter values the execution was started with |
+| `outputs` | json | Outputs the execution produced |
+| `stepExecutions` | json | Steps, each with stepName, action, stepStatus, stepExecutionId, executionStartTime, executionEndTime, failureMessage, response, isEnd, and nextStep |
+| `stepExecutionsTruncated` | boolean | Whether the returned step list was truncated |
+
+### SSM Stop Automation Execution
+
+Stop a running AWS Systems Manager Automation execution
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `automationExecutionId` | string | Yes | ID of the execution to stop, as returned by ssm_start_automation_execution |
+| `stopType` | string | No | How to stop the execution: Cancel to stop it immediately, or Complete to let the current step finish |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `message` | string | Operation status message |
+| `automationExecutionId` | string | ID of the execution that was stopped |
+
+### SSM List Documents
+
+List SSM documents and runbooks available to an AWS account
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `filters` | json | No | Filters, as an array of \{Key, Values\} objects. Valid keys: Name, Owner, DocumentType, PlatformTypes, TargetType, or tag:<key> |
+| `maxResults` | number | No | Maximum number of documents to return \(1-50\) |
+| `nextToken` | string | No | Pagination token from a previous request |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `documents` | json | Documents, each with name, displayName, owner, documentType, documentFormat, documentVersion, schemaVersion, platformTypes, targetType, createdDate, reviewStatus, author, and tags |
+| `nextToken` | string | Pagination token for the next page of results |
+| `count` | number | Number of documents returned |
+
+### SSM Get Document
+
+Read the content of an SSM document or Automation runbook
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `region` | string | Yes | AWS region \(e.g., us-east-1\) |
+| `accessKeyId` | string | Yes | AWS access key ID |
+| `secretAccessKey` | string | Yes | AWS secret access key |
+| `name` | string | Yes | Name of the document to read, as returned by ssm_list_documents |
+| `documentVersion` | string | No | Document version to read \($LATEST, $DEFAULT, or a version number\) |
+| `versionName` | string | No | User-defined version name to read |
+| `documentFormat` | string | No | Format to return the content in: JSON, YAML, or TEXT |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `name` | string | Name of the document |
+| `displayName` | string | Friendly name of the document |
+| `createdDate` | string | When the document was created |
+| `versionName` | string | User-defined version name |
+| `documentVersion` | string | Document version that was returned |
+| `status` | string | Document status \(Creating, Active, Updating, Deleting, Failed\) |
+| `statusInformation` | string | Detail about the document status |
+| `content` | string | Content of the document in the requested format |
+| `documentType` | string | Type of the document \(Command, Automation, Policy, Session, and related values\) |
+| `documentFormat` | string | Format the content is returned in |
+| `reviewStatus` | string | Review status of the document \(APPROVED, NOT_REVIEWED, PENDING, REJECTED\) |
+
+
diff --git a/apps/docs/content/docs/integrations/tinyfish.mdx b/apps/docs/content/docs/integrations/tinyfish.mdx
index 8afda30e85c..e1f2a9b8f6f 100644
--- a/apps/docs/content/docs/integrations/tinyfish.mdx
+++ b/apps/docs/content/docs/integrations/tinyfish.mdx
@@ -23,7 +23,8 @@ With TinyFish in Sim, you can:
- **Automate any site, API or not**: Log into vendor portals, legacy ERPs, and internal tools that never shipped an API, and pull the data out.
- **Get typed results, not scraped HTML**: Supply a JSON Schema in **Output Schema** and TinyFish holds the agent to it, re-prompting on mismatch and reporting every field that did not match in `schemaValidation`.
-- **Survive bot detection**: Switch **Browser Profile** to `stealth` for anti-detection, and enable the Tetra proxy with a country when the page is geo-restricted.
+- **Survive bot detection**: Switch **Browser Engine** to `stealth` for anti-detection, and enable the Tetra proxy with a country when the page is geo-restricted.
+- **Stay logged in between runs**: Enable **Use Browser Profile** to start a run from a Browser Context Profile — saved browser state you log into once on TinyFish. **List Browser Profiles** returns the ids to choose from; leaving the id empty uses your default profile. Pair it with the vault so an expired session can be repaired.
- **Log in safely**: Connect a password manager to TinyFish's vault, then enable **Use Vault Credentials** and scope a run to specific credential URIs. **List Vault Items** returns those URIs as display-safe metadata — labels, domains, field names — so credentials never travel through the workflow.
- **Run work that outlives a step**: **Start Agent Run** queues an automation and returns a run ID immediately; **Get Run**, **Cancel Run**, and **List Runs** track it afterwards, and a webhook URL can notify you on completion.
- **Read the live web cheaply**: Pair **Search** and **Fetch URLs** to gather current sources before an agent writes or answers.
@@ -74,14 +75,17 @@ Run a TinyFish web agent against a website and wait for it to finish, returning
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Target website URL the agent starts on |
| `goal` | string | Yes | Natural-language description of what to accomplish on the website |
-| `browserProfile` | string | No | Browser engine: "lite" \(standard\) or "stealth" \(anti-detection\) |
+| `browserProfile` | string | No | Browser engine: "lite" \(standard\) or "stealth" \(anti-detection\). Not a Browser Context Profile — use useProfile for saved logins |
| `agentMode` | string | No | Agent behavior: "default" or "strict" \(fail fast\) |
| `maxSteps` | number | No | Maximum tool-call steps before the agent stops \(1-500, default 150\) |
+| `maxDurationSeconds` | number | No | Maximum wall-clock seconds before the agent stops. Unlimited by default, so a run stalled on a slow page is only bounded by the step cap |
| `outputSchema` | json | No | JSON Schema draft-07 contract the run result must satisfy |
| `proxyEnabled` | boolean | No | Route the run through TinyFish’s Tetra proxy |
| `proxyCountryCode` | string | No | Proxy country: US, GB, CA, DE, FR, JP, or AU |
| `useVault` | boolean | No | Let the run use credentials from the connected TinyFish vault |
| `credentialItemIds` | string | No | Comma-separated vault credential URIs to scope the run to |
+| `useProfile` | boolean | No | Start the run from a saved Browser Context Profile, reusing the logged-in session stored in it |
+| `profileId` | string | No | Browser Context Profile to start from, such as "prof_abc123". Requires useProfile; omit to use the account default |
| `apiKey` | string | Yes | TinyFish API key |
#### Output
@@ -109,6 +113,10 @@ Run a TinyFish web agent against a website and wait for it to finish, returning
| ↳ `retryAfter` | number | Suggested retry delay in seconds, null when not retryable |
| ↳ `helpUrl` | string | Troubleshooting documentation URL |
| ↳ `helpMessage` | string | Human-readable guidance |
+| `profileHint` | object | Present when TinyFish believes a Browser Context Profile would fix this failed run, such as one that stopped at a login wall |
+| ↳ `message` | string | Why a Browser Context Profile would help this run |
+| ↳ `setupUrl` | string | Path on the TinyFish dashboard that sets up a profile for the blocked domain |
+| ↳ `reason` | string | auth_wall \(the run hit a login\) or bot_challenge \(the site blocked automation\) |
### TinyFish Start Agent Run
@@ -120,14 +128,17 @@ Queue a TinyFish web agent run and return its run ID immediately, without waitin
| --------- | ---- | -------- | ----------- |
| `url` | string | Yes | Target website URL the agent starts on |
| `goal` | string | Yes | Natural-language description of what to accomplish on the website |
-| `browserProfile` | string | No | Browser engine: "lite" \(standard\) or "stealth" \(anti-detection\) |
+| `browserProfile` | string | No | Browser engine: "lite" \(standard\) or "stealth" \(anti-detection\). Not a Browser Context Profile — use useProfile for saved logins |
| `agentMode` | string | No | Agent behavior: "default" or "strict" \(fail fast\) |
| `maxSteps` | number | No | Maximum tool-call steps before the agent stops \(1-500, default 150\) |
+| `maxDurationSeconds` | number | No | Maximum wall-clock seconds before the agent stops. Unlimited by default, so a run stalled on a slow page is only bounded by the step cap |
| `outputSchema` | json | No | JSON Schema draft-07 contract the run result must satisfy |
| `proxyEnabled` | boolean | No | Route the run through TinyFish’s Tetra proxy |
| `proxyCountryCode` | string | No | Proxy country: US, GB, CA, DE, FR, JP, or AU |
| `useVault` | boolean | No | Let the run use credentials from the connected TinyFish vault |
| `credentialItemIds` | string | No | Comma-separated vault credential URIs to scope the run to |
+| `useProfile` | boolean | No | Start the run from a saved Browser Context Profile, reusing the logged-in session stored in it |
+| `profileId` | string | No | Browser Context Profile to start from, such as "prof_abc123". Requires useProfile; omit to use the account default |
| `apiKey` | string | Yes | TinyFish API key |
| `webhookUrl` | string | No | HTTPS URL notified when the run completes, fails, or is cancelled |
@@ -179,6 +190,12 @@ Get the status, extracted result, and step history of a TinyFish automation run
| `browserConfig` | object | Proxy settings the run executed with |
| ↳ `proxyEnabled` | boolean | Whether a proxy was used |
| ↳ `proxyCountryCode` | string | Proxy country code |
+| `profileAttached` | boolean | Whether the run actually started from a Browser Context Profile, null when the API omits it — treat null as unknown rather than as false |
+| `profileId` | string | Browser Context Profile the run attached. Null covers both no profile and a payload that omitted the field, so read profileAttached alongside it rather than reading null as proof |
+| `profileHint` | object | Present when TinyFish believes a Browser Context Profile would fix this failed run |
+| ↳ `message` | string | Why a Browser Context Profile would help this run |
+| ↳ `setupUrl` | string | Path on the TinyFish dashboard that sets up a profile for the blocked domain |
+| ↳ `reason` | string | auth_wall \(the run hit a login\) or bot_challenge \(the site blocked automation\) |
| `videoUrl` | string | Presigned recording URL, expires 15 minutes after it is issued |
| `steps` | array | Steps the agent took during the run |
| ↳ `id` | string | Step identifier |
@@ -256,6 +273,12 @@ List TinyFish automation runs, optionally filtered by status, goal text, or crea
| ↳ `browserConfig` | object | Proxy settings the run executed with |
| ↳ `proxyEnabled` | boolean | Whether a proxy was used |
| ↳ `proxyCountryCode` | string | Proxy country code |
+| ↳ `profileAttached` | boolean | Whether the run actually started from a Browser Context Profile, null when the API omits it — treat null as unknown rather than as false |
+| ↳ `profileId` | string | Browser Context Profile the run attached. Null covers both no profile and a payload that omitted the field, so read profileAttached alongside it rather than reading null as proof |
+| ↳ `profileHint` | object | Present when TinyFish believes a Browser Context Profile would fix this failed run |
+| ↳ `message` | string | Why a Browser Context Profile would help this run |
+| ↳ `setupUrl` | string | Path on the TinyFish dashboard that sets up a profile for the blocked domain |
+| ↳ `reason` | string | auth_wall \(the run hit a login\) or bot_challenge \(the site blocked automation\) |
| `total` | number | Total runs matching the filters |
| `nextCursor` | string | Cursor for the next page, null when there are no more results |
| `hasMore` | boolean | Whether more results follow this page |
@@ -347,4 +370,28 @@ List the credentials available from password managers connected to TinyFish, wit
| ↳ `type` | string | STRING, CONCEALED, or OTP |
| ↳ `hasTotp` | boolean | Whether the credential carries a TOTP secret |
+### TinyFish List Browser Profiles
+
+List the Browser Context Profiles saved on the TinyFish account, with the ids an agent run can start from to reuse a logged-in session
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | TinyFish API key |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `profiles` | array | Browser Context Profiles an agent run can start from |
+| ↳ `profileId` | string | Profile identifier, used as the Browser Profile ID on a run |
+| ↳ `name` | string | Profile name, such as "Salesforce Production" |
+| ↳ `proxyCountryCode` | string | Country the profile proxies through, null when it has no proxy |
+| ↳ `fingerprintSeed` | string | Seed for the browser fingerprint the profile replays, null when the API omits it |
+| ↳ `domainCount` | number | How many domains the profile holds saved state for, null when the API omits it. Zero means it was created but never logged into |
+| ↳ `createdAt` | string | ISO 8601 timestamp when the profile was created, null when the API omits it |
+| ↳ `updatedAt` | string | ISO 8601 timestamp when the profile was last saved, null when the API omits it |
+| ↳ `isDefault` | boolean | Whether runs with no Browser Profile ID use this one, null when the API omits it |
+
diff --git a/apps/docs/content/docs/keyboard-shortcuts/index.mdx b/apps/docs/content/docs/keyboard-shortcuts/index.mdx
index e541c05841a..feffd0a6a83 100644
--- a/apps/docs/content/docs/keyboard-shortcuts/index.mdx
+++ b/apps/docs/content/docs/keyboard-shortcuts/index.mdx
@@ -5,7 +5,7 @@ description: Keyboard and mouse shortcuts for the workflow editor and for tables
import { Callout } from 'fumadocs-ui/components/callout'
-Sim has keyboard shortcuts for the workflow editor and for tables. Each set works when that surface is focused and you are not typing in a field.
+Sim has keyboard shortcuts for the workflow editor and for tables. Each set works when that surface is focused and you are not typing in a field. The macOS app adds its own window, tab, and navigation shortcuts on top of these — see [Sim Desktop](/desktop#keyboard-shortcuts).
**Mod** is `Cmd` on macOS and `Ctrl` on Windows and Linux.
diff --git a/apps/docs/content/docs/knowledgebase/index.mdx b/apps/docs/content/docs/knowledgebase/index.mdx
index a2d79a0d58a..3b7940516b5 100644
--- a/apps/docs/content/docs/knowledgebase/index.mdx
+++ b/apps/docs/content/docs/knowledgebase/index.mdx
@@ -23,6 +23,8 @@ When you upload a document, Sim processes it in the background:
A document is searchable once its status reads `completed`. Open any document to view, edit, merge, or split its chunks.
+Every knowledge base records the embedding model and the vector width it was built with, and keeps them for its lifetime. Two bases built differently cannot be searched in one request — vectors are only comparable when they come from the same model at the same size — so search them separately, or rebuild one to match. On Sim Cloud every base uses `text-embedding-3-small` at 1,536 dimensions; self-hosted deployments choose both with [`KB_EMBEDDING_MODEL` and `EMBEDDING_OUTPUT_DIMS`](/platform/self-hosting/environment-variables), including models on their own Ollama.
+
## What you can upload
Sim accepts PDF, Word, text, Markdown, HTML, Excel, PowerPoint, CSV, JSON, and YAML files, up to 100 MB each (best under 50 MB). Scanned PDFs work too: with Azure or [Mistral OCR](https://docs.mistral.ai/ocr/) configured, Sim extracts text from image-based pages.
diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json
index 826e392a3ff..fd86838d469 100644
--- a/apps/docs/content/docs/meta.json
+++ b/apps/docs/content/docs/meta.json
@@ -4,6 +4,7 @@
"---Get Started---",
"./introduction/index",
"./getting-started/index",
+ "./desktop/index",
"---Build---",
"chat",
"workflows",
diff --git a/apps/docs/content/docs/platform/self-hosting/desktop.mdx b/apps/docs/content/docs/platform/self-hosting/desktop.mdx
index 8452a43628b..43c153a16a3 100644
--- a/apps/docs/content/docs/platform/self-hosting/desktop.mdx
+++ b/apps/docs/content/docs/platform/self-hosting/desktop.mdx
@@ -1,11 +1,13 @@
---
-title: Desktop App
+title: Desktop App on Your Deployment
description: Point the macOS desktop app at your own Sim deployment
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
+This page is about pointing the app at a deployment you run. For installing and using it, see [Sim Desktop](/desktop).
+
The Sim desktop app is a native macOS shell around a Sim deployment. It is **not** tied to sim.ai — the build bakes in only a *default* server, and every runtime boundary (navigation, content security policy, cookie storage, the update feed) is derived from the server you point it at.
So self-hosting the desktop app takes no build of your own: install the same signed, notarized app everyone else installs, then point it at your deployment.
@@ -23,7 +25,7 @@ Every Sim deployment exposes two public endpoints:
| `/api/desktop/update/download` | Redirects (302) to the newest installer for this deployment's release channel |
| `/api/desktop/update/latest-mac.yml` | The update manifest installed apps poll |
-Both resolve against Sim's public GitHub releases, and the installers themselves are downloaded from GitHub. Nothing is built, signed, or hosted by you: your deployment decides *which* release its clients are offered and serves the manifest, so installed apps poll your server instead of sim.ai — but they cannot be served artifacts of your own from this path. To ship your own build, see [Building your own shell](#building-your-own-shell).
+Both resolve against Sim's public GitHub releases, and the installers themselves are downloaded from GitHub. Nothing is built, signed, or hosted by you: your deployment decides *which* release its clients are offered and serves the manifest, so installed apps poll your server instead of sim.ai — but they cannot be served artifacts of your own from this path, and a self-updating stable build falls back to Sim's own feed if yours stops answering. To ship your own build, see [Building your own shell](#building-your-own-shell).
Both endpoints cache their GitHub lookups for **5 minutes**, and both respond the same way when they cannot answer:
@@ -35,6 +37,12 @@ Both endpoints cache their GitHub lookups for **5 minutes**, and both respond th
The Sim server needs outbound access to `api.github.com` and `github.com` for these to resolve. Unauthenticated GitHub API requests are capped at 60/hour per IP; set `GITHUB_TOKEN` on the Sim server to raise that to 5000/hour.
+### What an installed app does when the feed fails
+
+A `404` carries a header the shell reads as *no update available*, so the app reports it is up to date. Anything else — `502`, a timeout, a connection failure — means the feed is unavailable, and only a self-updating build (Developer ID, installed in `/Applications`) has anywhere else to go: on the stable channel it falls back to Sim's packaged GitHub feed, and on `dev` or `staging` it skips the check, since that fallback carries only stable artifacts their bundle identity cannot install. Every other build simply stops being offered updates until the feed answers again.
+
+Release selection is therefore yours only while the feed answers. With `APPCONFIG_ENVIRONMENT` unset both paths resolve the same release, so the fallback is invisible; it matters when your deployment serves a channel that disagrees with stable.
+
### Which channel your deployment serves
The channel is chosen by `APPCONFIG_ENVIRONMENT`, not by whether you are self-hosting:
diff --git a/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx b/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx
index 53a241d7d41..8dbcf1ba31a 100644
--- a/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx
+++ b/apps/docs/content/docs/platform/self-hosting/environment-variables.mdx
@@ -55,7 +55,7 @@ import { Callout } from 'fumadocs-ui/components/callout'
| `VERTEX_PROJECT` / `VERTEX_LOCATION` | Google Vertex AI — GCP project ID and region (region defaults to `us-central1`) |
- **Knowledge bases require a hosted embedding provider.** Three are supported, selected with `KB_EMBEDDING_MODEL`: `text-embedding-3-small` (default) and `text-embedding-3-large` on OpenAI or Azure OpenAI, and `gemini-embedding-001` on Gemini. There is no local embedding backend — configuring Ollama or vLLM does not substitute, because embeddings do not route through the configured chat model.
+ **Knowledge bases need an embedding model, selected with `KB_EMBEDDING_MODEL`.** Hosted: `text-embedding-3-small` (default) and `text-embedding-3-large` on OpenAI or Azure OpenAI, and `gemini-embedding-001` on Gemini. Local: any embedding model on your own Ollama, named `ollama/` (for example `ollama/nomic-embed-text`) with `OLLAMA_URL` set. Ollama serves embeddings only through this variable — embeddings never route through the configured chat model, so vLLM and LM Studio do not substitute.
@@ -190,10 +190,43 @@ See [Observability](/platform/self-hosting/observability).
| Variable | Description |
|----------|-------------|
-| `KB_EMBEDDING_MODEL` | Embedding model for new knowledge bases. Defaults to `text-embedding-3-small`; an unsupported value falls back to the default |
+| `KB_EMBEDDING_MODEL` | Embedding model for new knowledge bases. Defaults to `text-embedding-3-small`; use `ollama/` for a model on your own Ollama. An unrecognised hosted model id falls back to the default, but an `ollama/` id is taken at face value — if that model is not on the server, knowledge-base creation fails rather than falling back |
+| `EMBEDDING_OUTPUT_DIMS` | Vector width new knowledge bases are stored at: `384`, `768`, `1024`, `1536` (default), or `3072`. It must be a width the chosen model can emit; anything else falls back to `1536` with a warning |
| `OPENROUTER_API_KEY` | Fallback route for the OpenAI embedding models — used when it is set and `OPENAI_API_KEY` is not the chosen path |
| `COHERE_API_KEY` | Enables the Knowledge block reranker |
+Both variables apply at creation time and are recorded on the knowledge base, so changing either
+affects new knowledge bases only. Existing ones keep the model and width they were built with, and
+knowledge bases with different settings cannot be searched together.
+
+Matching the width to the model is yours to get right for a hosted provider: Sim knows only the
+widths each one documents, so a width that model cannot emit falls back to `1536` with a warning.
+For Ollama it is checked — leave `EMBEDDING_OUTPUT_DIMS` unset and Sim reads the model's width from
+your server, refusing to create the knowledge base if it cannot; set it explicitly and a mismatch
+surfaces as a failed document instead. Common pairings:
+
+| `EMBEDDING_OUTPUT_DIMS` | Works with |
+|----------|-------------|
+| `384` | `ollama/all-minilm` |
+| `768` | `ollama/nomic-embed-text`, `ollama/embeddinggemma`, `text-embedding-3-small`, `gemini-embedding-001` |
+| `1024` | `ollama/mxbai-embed-large`, `ollama/bge-m3`, `text-embedding-3-small`, `text-embedding-3-large` |
+| `1536` | `text-embedding-3-small`, `text-embedding-3-large`, `gemini-embedding-001` |
+| `3072` | `text-embedding-3-large`, `gemini-embedding-001` |
+
+On a Compose install or source checkout, `sim-setup add knowledge-embeddings` walks through all of
+this — pick OpenAI, Azure OpenAI, OpenRouter, Gemini, or Ollama and it writes the variables that
+family needs. The three OpenAI-family transports share the same model and width prompts.
+`sim-setup config` then reports the one family your `KB_EMBEDDING_MODEL` actually selects, rather
+than every provider you happen to hold a key for. Helm releases set these values through your own
+chart values instead, and an Ollama server is yours to run either way — `sim-setup` configures Sim
+to reach one, never installs it.
+
+The Embeddings block reads the same `OLLAMA_URL`. It lists the models on that server that report an
+embedding capability, with the width each one emits where Ollama reports it, so a workflow can embed
+locally without an API key. Ollama only began reporting capabilities in 0.5 — against an older
+server nothing can be filtered, so the list includes chat models and labels none of them with a
+width. Check what you pick there, or upgrade Ollama.
+
## Chat & PII
| Variable | Description |
diff --git a/apps/docs/content/docs/platform/self-hosting/index.mdx b/apps/docs/content/docs/platform/self-hosting/index.mdx
index ce75aa68058..7cbfc77249b 100644
--- a/apps/docs/content/docs/platform/self-hosting/index.mdx
+++ b/apps/docs/content/docs/platform/self-hosting/index.mdx
@@ -109,7 +109,7 @@ Sim is self-contained for the core editor and execution engine. A few features r
| Feature | Requires | Notes |
|---|---|---|
-| **Knowledge bases** | An OpenAI, Azure OpenAI, or Gemini API key | Embeddings are generated by a hosted provider, selected with `KB_EMBEDDING_MODEL` (`text-embedding-3-small` by default). There is no local embedding backend — knowledge bases are unavailable without one of these keys. |
+| **Knowledge bases** | An OpenAI, Azure OpenAI, or Gemini API key, or an Ollama server | Embeddings are generated by the model named in `KB_EMBEDDING_MODEL` (`text-embedding-3-small` by default). Set it to `ollama/` with `OLLAMA_URL` to embed locally instead. `EMBEDDING_OUTPUT_DIMS` sets the vector width. |
| **Agent blocks** | An API key for at least one model provider | Or a self-hosted OpenAI-compatible endpoint: Ollama, vLLM, LM Studio, or LiteLLM. |
| **Chat module** | `COPILOT_API_KEY` from sim.ai | Set `NEXT_PUBLIC_CHAT_DISABLED=true` to hide the module instead. |
| **Integrations** | Your own OAuth app per service | See [Integrations & OAuth](/platform/self-hosting/integrations-oauth). |
diff --git a/apps/docs/content/docs/platform/self-hosting/troubleshooting.mdx b/apps/docs/content/docs/platform/self-hosting/troubleshooting.mdx
index 0e07620595a..1bfa5e25130 100644
--- a/apps/docs/content/docs/platform/self-hosting/troubleshooting.mdx
+++ b/apps/docs/content/docs/platform/self-hosting/troubleshooting.mdx
@@ -250,7 +250,9 @@ The load balancer's backend timeout is closing them. On GKE, attach a `BackendCo
## Knowledge base upload fails
-Embeddings need a hosted provider — set `OPENAI_API_KEY`, configure Azure OpenAI, or set `KB_EMBEDDING_MODEL=gemini-embedding-001` with a Gemini key. There is no local embedding backend, so Ollama or vLLM does not substitute. If a key is set, verify pgvector is installed on the database.
+Embeddings need a provider — set `OPENAI_API_KEY`, configure Azure OpenAI, set `KB_EMBEDDING_MODEL=gemini-embedding-001` with a Gemini key, or set `KB_EMBEDDING_MODEL=ollama/` with `OLLAMA_URL` to embed on your own Ollama. On a self-hosted deployment `OPENROUTER_API_KEY` also serves the OpenAI-family models on its own, as a fallback behind any OpenAI or Azure credentials you have. If one is configured, verify pgvector is installed on the database.
+
+A document that fails with `vector 0 has N unexpected dimensions` means `EMBEDDING_OUTPUT_DIMS` does not match what the model actually emits. The message names both widths. If the width the model returned is one of `384`, `768`, `1024`, `1536`, or `3072`, set the variable to it and recreate the knowledge base. If it is anything else, no column can store it — choose a model that emits one of those five instead, since setting an unstorable width silently falls back to `1536` and the next document fails the same way. Existing knowledge bases keep the width they were created with.
## Credentials Unreadable After a Restore
diff --git a/apps/docs/content/docs/platform/self-hosting/verify.mdx b/apps/docs/content/docs/platform/self-hosting/verify.mdx
index 49d6c020b68..f902981ac2e 100644
--- a/apps/docs/content/docs/platform/self-hosting/verify.mdx
+++ b/apps/docs/content/docs/platform/self-hosting/verify.mdx
@@ -47,7 +47,7 @@ The full command list is in the [Docker guide](/platform/self-hosting/docker#the
| 5 | Paste a model API key in settings and run a two-block workflow | Execution engine, credential encryption, outbound network | App logs; check `ENCRYPTION_KEY` is set and outbound egress is allowed |
| 6 | Upload a small file in Files | File storage end to end | With object storage configured: presigned URL + bucket CORS. On local disk: the upload proxies through the app |
| 7 | Upload a file larger than 50 MB | Multipart upload path (object storage only) | Check app logs for provider part-listing or completion errors |
-| 8 | Create a knowledge base and upload a PDF | Document parsing, embeddings, pgvector | Needs a hosted embedding provider — see below |
+| 8 | Create a knowledge base and upload a PDF | Document parsing, embeddings, pgvector | Needs an embedding provider — see below |
| 9 | Invite a teammate from workspace settings | Email delivery | App logs for the mailer; see [Email](/platform/self-hosting/email) |
| 10 | Connect an integration account | OAuth configuration | Redirect URI mismatch → see [Integrations & OAuth](/platform/self-hosting/integrations-oauth) |
| 11 | Create a workflow with a Schedule trigger set to every minute, deploy it, wait 2 minutes | **Background jobs** | Check the scheduler's logs — see [Background Jobs](/platform/self-hosting/background-jobs) |
@@ -102,7 +102,7 @@ All six should be present on Compose: `simstudio`, `realtime`, `db`, `redis`, `c
**Step 6 or 7 fails.** With object storage configured, a CORS error in the browser console means the bucket policy does not allow your Sim origin or the signed upload headers. If step 7 fails only during completion, check the app logs and verify the server identity can list multipart parts (for S3, `s3:ListMultipartUploadParts`). On local-disk storage there is no CORS involved — uploads proxy through the app, so look at the app logs and the proxy body-size limit instead.
-**Step 8 fails — knowledge base upload errors.** Knowledge bases need a hosted embedding provider — OpenAI, Azure OpenAI, or Gemini. There is no local embedding backend. If a key is set, check pgvector is installed on the database.
+**Step 8 fails — knowledge base upload errors.** Knowledge bases need an embedding provider: OpenAI, Azure OpenAI, or Gemini with an API key, or a model on your own Ollama via `KB_EMBEDDING_MODEL=ollama/` and `OLLAMA_URL`. If one is configured, check pgvector is installed on the database.
**Step 9 fails — no email arrives.** With no provider configured the mailer no-ops: it records the recipient, subject, and sender at `info` and reports success, never the message body. Raise `LOG_LEVEL` to `INFO` to see that line — the variable is uppercase-only, and at the production default of `ERROR` nothing is logged at all.
diff --git a/apps/docs/content/docs/workflows/blocks/agent.mdx b/apps/docs/content/docs/workflows/blocks/agent.mdx
index 7b29b8e7e00..c0acf1714d8 100644
--- a/apps/docs/content/docs/workflows/blocks/agent.mdx
+++ b/apps/docs/content/docs/workflows/blocks/agent.mdx
@@ -108,8 +108,8 @@ Live tool-call chips stream for **OpenAI, Anthropic, Azure Anthropic, Google, Ve
| Provider | Streamed thinking | Models |
|----------|-------------------|--------|
-| OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5-pro`, `gpt-5.5`, `gpt-5.4-pro`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.2-pro`, `gpt-5.2`, `gpt-5.1`, `gpt-5-pro`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `o4-mini`, `o3`, `o3-mini`, `o1` |
-| Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `claude-fable-5-1`, `claude-fable-5`, `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5`, `claude-opus-4-1`, `claude-sonnet-4-5`, `claude-haiku-4-5` |
+| OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `gpt-6-astra`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5-pro`, `gpt-5.5`, `gpt-5.4-pro`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.2-pro`, `gpt-5.2`, `gpt-5.1`, `gpt-5-pro`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `o4-mini`, `o3`, `o3-mini`, `o1` |
+| Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `claude-fable-5-1`, `claude-fable-5`, `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |
| Azure OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `azure/gpt-5.4`, `azure/gpt-5.4-mini`, `azure/gpt-5.4-nano`, `azure/gpt-5.2`, `azure/gpt-5.1`, `azure/gpt-5.1-codex`, `azure/gpt-5`, `azure/gpt-5-mini`, `azure/gpt-5-nano`, `azure/o3`, `azure/o4-mini` |
| Azure Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `azure-anthropic/claude-opus-4-6`, `azure-anthropic/claude-opus-4-5`, `azure-anthropic/claude-sonnet-4-5`, `azure-anthropic/claude-opus-4-1`, `azure-anthropic/claude-haiku-4-5` |
| Google | Summaries only | `gemini-3.8-flash`, `gemini-3.6-flash`, `gemini-3.5-flash-lite`, `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite` |
diff --git a/apps/docs/openapi-v2-resources.json b/apps/docs/openapi-v2-resources.json
index d6556294305..c2ff564af75 100644
--- a/apps/docs/openapi-v2-resources.json
+++ b/apps/docs/openapi-v2-resources.json
@@ -7498,6 +7498,90 @@
"additionalProperties": false
},
"description": "Authorization servers available for this OAuth service."
+ },
+ "fields": {
+ "maxItems": 20,
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Exact create-body field name."
+ },
+ "label": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Human-readable field label."
+ },
+ "placeholder": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 1000,
+ "description": "Suggested input placeholder."
+ },
+ "required": {
+ "type": "boolean",
+ "description": "Whether the field is required for the selected flow."
+ },
+ "secret": {
+ "type": "boolean",
+ "description": "Whether the submitted field is write-only secret material."
+ },
+ "multiline": {
+ "type": "boolean",
+ "description": "Whether the field is intended for multi-line input."
+ },
+ "requiredForAuthMethods": {
+ "description": "Authentication methods for which this field is required.",
+ "minItems": 1,
+ "maxItems": 10,
+ "type": "array",
+ "items": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 64
+ }
+ },
+ "options": {
+ "description": "Fixed values accepted by a selector field.",
+ "minItems": 1,
+ "maxItems": 20,
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "value": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Submitted option value."
+ },
+ "label": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Human-readable option label."
+ }
+ },
+ "required": ["value", "label"],
+ "additionalProperties": false
+ }
+ },
+ "hint": {
+ "description": "Provider-specific setup guidance.",
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 2000
+ }
+ },
+ "required": ["id", "label", "placeholder", "required", "secret", "multiline"],
+ "additionalProperties": false
+ },
+ "description": "Write-only setup fields required before starting this OAuth flow."
}
},
"required": [
@@ -7508,7 +7592,8 @@
"providerFamily",
"available",
"supportsReconnect",
- "authorizationOptions"
+ "authorizationOptions",
+ "fields"
],
"additionalProperties": false
},
@@ -7710,6 +7795,7 @@
"providerFamily": "salesforce",
"available": true,
"supportsReconnect": true,
+ "fields": [],
"authorizationOptions": [
{
"providerId": "salesforce",
@@ -7886,29 +7972,147 @@
"CreateCredentialConnectionBody": {
"anyOf": [
{
- "type": "object",
- "properties": {
- "workspaceId": {
- "type": "string",
- "minLength": 1,
- "maxLength": 128,
- "description": "Workspace that will own the credential."
- },
- "providerId": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255,
- "description": "Exact OAuth provider ID returned by credential-provider discovery."
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "workspaceId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "Workspace that will own the credential."
+ },
+ "displayName": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Name shown for the new credential in Sim."
+ },
+ "providerId": {
+ "type": "string",
+ "const": "quickbooks",
+ "description": "QuickBooks OAuth provider ID returned by credential-provider discovery."
+ },
+ "oauthClientConfig": {
+ "type": "object",
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Client ID for the caller-managed Intuit OAuth application."
+ },
+ "clientSecret": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512,
+ "description": "Write-only client secret for the caller-managed Intuit OAuth application.",
+ "writeOnly": true
+ },
+ "environment": {
+ "type": "string",
+ "enum": ["sandbox", "production"],
+ "description": "Intuit company environment used for authorization and API requests."
+ },
+ "webhookVerifierToken": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512,
+ "description": "Write-only verifier token for webhook signatures from the caller-managed app.",
+ "writeOnly": true
+ }
+ },
+ "required": ["clientId", "clientSecret", "environment", "webhookVerifierToken"],
+ "additionalProperties": false,
+ "description": "Write-only caller-managed Intuit OAuth app configuration."
+ }
+ },
+ "required": ["workspaceId", "displayName", "providerId", "oauthClientConfig"],
+ "additionalProperties": false
},
- "displayName": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255,
- "description": "Name shown for the new credential in Sim."
+ {
+ "type": "object",
+ "properties": {
+ "workspaceId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "Workspace that will own the credential."
+ },
+ "displayName": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Name shown for the new credential in Sim."
+ },
+ "providerId": {
+ "type": "string",
+ "enum": [
+ "google-email",
+ "google-drive",
+ "google-docs",
+ "google-sheets",
+ "google-forms",
+ "google-calendar",
+ "google-contacts",
+ "google-ads",
+ "google-bigquery",
+ "google-tasks",
+ "google-vault",
+ "google-groups",
+ "google-chat",
+ "google-meet",
+ "vertex-ai",
+ "microsoft-ad",
+ "microsoft-dataverse",
+ "microsoft-excel",
+ "microsoft-planner",
+ "microsoft-teams",
+ "microsoft-word",
+ "outlook",
+ "onedrive",
+ "sharepoint",
+ "x",
+ "tiktok",
+ "confluence",
+ "jira",
+ "airtable",
+ "bitbucket",
+ "notion",
+ "clickup",
+ "linear",
+ "manageengine-sdp",
+ "monday",
+ "box",
+ "dropbox",
+ "shopify",
+ "slack",
+ "reddit",
+ "wealthbox",
+ "webflow",
+ "trello",
+ "asana",
+ "attio",
+ "calcom",
+ "docusign",
+ "pipedrive",
+ "hubspot",
+ "linkedin",
+ "instagram",
+ "salesforce",
+ "salesforce-sandbox",
+ "zoho-desk",
+ "zoom",
+ "wordpress",
+ "spotify"
+ ],
+ "description": "Exact OAuth provider ID returned by credential-provider discovery."
+ }
+ },
+ "required": ["workspaceId", "displayName", "providerId"],
+ "additionalProperties": false
}
- },
- "required": ["workspaceId", "providerId", "displayName"],
- "additionalProperties": false
+ ]
},
{
"type": "object",
@@ -7923,7 +8127,40 @@
"type": "string",
"minLength": 1,
"maxLength": 255,
- "description": "Existing OAuth credential to reconnect in place."
+ "description": "Existing OAuth credential to reconnect in place. QuickBooks reconnects also require oauthClientConfig with the Intuit client ID, client secret, environment, and webhook verifier token."
+ },
+ "oauthClientConfig": {
+ "description": "Write-only Intuit OAuth app configuration. Required when credentialId identifies a QuickBooks credential; omit it for other providers.",
+ "type": "object",
+ "properties": {
+ "clientId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "Client ID for the caller-managed Intuit OAuth application."
+ },
+ "clientSecret": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512,
+ "description": "Write-only client secret for the caller-managed Intuit OAuth application.",
+ "writeOnly": true
+ },
+ "environment": {
+ "type": "string",
+ "enum": ["sandbox", "production"],
+ "description": "Intuit company environment used for authorization and API requests."
+ },
+ "webhookVerifierToken": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512,
+ "description": "Write-only verifier token for webhook signatures from the caller-managed app.",
+ "writeOnly": true
+ }
+ },
+ "required": ["clientId", "clientSecret", "environment", "webhookVerifierToken"],
+ "additionalProperties": false
}
},
"required": ["workspaceId", "credentialId"],
diff --git a/apps/docs/public/static/desktop/sim-desktop.png b/apps/docs/public/static/desktop/sim-desktop.png
new file mode 100644
index 00000000000..0e09b392b84
Binary files /dev/null and b/apps/docs/public/static/desktop/sim-desktop.png differ
diff --git a/apps/realtime/package.json b/apps/realtime/package.json
index 78b86958d99..4a90594d62c 100644
--- a/apps/realtime/package.json
+++ b/apps/realtime/package.json
@@ -5,7 +5,7 @@
"license": "Apache-2.0",
"type": "module",
"engines": {
- "bun": ">=1.3.14",
+ "bun": ">=1.4.1",
"node": ">=20.0.0"
},
"scripts": {
diff --git a/apps/realtime/src/handlers/file-doc.test.ts b/apps/realtime/src/handlers/file-doc.test.ts
index 4ba34c83de1..c0878d90129 100644
--- a/apps/realtime/src/handlers/file-doc.test.ts
+++ b/apps/realtime/src/handlers/file-doc.test.ts
@@ -233,12 +233,13 @@ describe('setupWorkspaceFileDocHandlers', () => {
)
})
- it('rejects a payload missing the file id or client id before authorizing', async () => {
+ it('rejects a payload with a missing or out-of-range client id before authorizing', async () => {
const { io } = createIo()
const { socket, handlers } = setup('socket-1', io)
await handlers[FILE_DOC_EVENTS.JOIN]({ fileId: '', clientId: 1 })
await handlers[FILE_DOC_EVENTS.JOIN]({ fileId: 'file-1' })
+ await handlers[FILE_DOC_EVENTS.JOIN]({ fileId: 'file-1', clientId: 0x1_0000_0000 })
expect(socket.emit).toHaveBeenCalledWith(
FILE_DOC_EVENTS.JOIN_ERROR,
@@ -545,6 +546,10 @@ describe('setupWorkspaceFileDocHandlers', () => {
expect(socket.join).toHaveBeenCalledWith(ROOM_NAME)
expect(joinSuccessFileId(socket)).toBe('file-1')
+ expect(socket.emit).toHaveBeenCalledWith(
+ FILE_DOC_EVENTS.JOIN_SUCCESS,
+ expect.objectContaining({ fileId: 'file-1', clientId: 1 })
+ )
// A binary sync-step-1 message (type tag 0) is sent to kick off the handshake.
const syncMessage = socket.emit.mock.calls.find(
@@ -843,7 +848,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
expect(mockFetchFileDocMerge).toHaveBeenCalledTimes(2)
})
- it('relays a document update to the rest of the room, excluding the sender', async () => {
+ it('relays a document update to every provider, including siblings on the sender socket', async () => {
const { io, sent } = createIo()
const a = setup('socket-a', io)
const b = setup('socket-b', io)
@@ -860,7 +865,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
const relayed = sent.find((m) => m.event === FILE_DOC_EVENTS.MESSAGE)
expect(relayed?.target).toBe(ROOM_NAME)
- expect(relayed?.except).toBe('socket-a')
+ expect(relayed?.except).toBeUndefined()
expect((relayed?.payload as Uint8Array)[0]).toBe(FILE_DOC_MESSAGE_TYPE.SYNC)
})
@@ -959,6 +964,35 @@ describe('setupWorkspaceFileDocHandlers', () => {
expect(relayedFor(999)).toBeUndefined()
})
+ it('accepts concurrent joins from co-mounted providers for the same file', async () => {
+ let resolveFirstAuth: (value: unknown) => void = () => {}
+ mockAuthorizeRoom.mockReturnValueOnce(
+ new Promise((resolve) => {
+ resolveFirstAuth = resolve
+ })
+ )
+ const { io } = createIo()
+ const { socket, handlers } = setup('socket-a', io)
+
+ const first = handlers[FILE_DOC_EVENTS.JOIN]({ fileId: 'file-1', clientId: 500 })
+ await Promise.resolve()
+ const second = handlers[FILE_DOC_EVENTS.JOIN]({ fileId: 'file-1', clientId: 501 })
+ await second
+
+ resolveFirstAuth({
+ allowed: true,
+ status: 200,
+ workspaceId: 'ws-1',
+ workspacePermission: 'write',
+ })
+ await first
+
+ const acceptedClientIds = socket.emit.mock.calls
+ .filter(([event]) => event === FILE_DOC_EVENTS.JOIN_SUCCESS)
+ .map(([, payload]) => (payload as { clientId: number }).clientId)
+ expect(acceptedClientIds).toEqual(expect.arrayContaining([500, 501]))
+ })
+
it('preserves the existing caret when a rebind to a foreign client id is rejected', async () => {
const { io, sent } = createIo()
const { frame: awFrame } = awarenessFrame(10, 'A')
diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts
index 1e29f476de9..28ef6686f7a 100644
--- a/apps/realtime/src/handlers/file-doc.ts
+++ b/apps/realtime/src/handlers/file-doc.ts
@@ -192,15 +192,19 @@ const fileDocRooms = new Map()
/** socketId → its current file-doc room name (a socket edits at most one doc). */
const socketToRoomName = new Map()
/**
- * socketId → a monotonic join generation. A JOIN bumps it on arrival and, after
- * the async authorization, proceeds only if the generation is still its own — so
- * a newer JOIN (a fast document switch) or a disconnect (which drops the entry in
- * cleanup) that occurred during authorization aborts the now-stale JOIN. Without
- * this, an out-of-order authorize completion could bind the socket to the wrong
- * document, or a disconnect-during-authorize could register a dead socket and
- * leak its room.
+ * socketId → a monotonic file-intent generation. Switching files or leaving the
+ * intended file advances it; co-mounted providers joining the same file share it.
+ * After async authorization, a join proceeds only while its generation is current,
+ * preventing an out-of-order completion from binding the socket to the wrong file.
*/
const joinGeneration = new Map()
+const MAX_YJS_CLIENT_ID = 0xffff_ffff
+
+function isYjsClientId(value: unknown): value is number {
+ return (
+ typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= MAX_YJS_CLIENT_ID
+ )
+}
interface AwarenessChange {
added: number[]
@@ -227,10 +231,8 @@ function originSocketId(origin: unknown): string | null {
* The transaction origin stamped on an agent-streamed frame (a {@link FILE_DOC_MESSAGE_TYPE.SYNC_NO_PERSIST}
* apply). A non-string sentinel, so `originSocketId` returns `null` for it and the update never triggers
* `edited`/`schedulePersist` (the copilot's final `edit_content` write is the durable persist). Unlike a
- * client edit, an agent frame is broadcast to the WHOLE room (its originating socket is NOT excluded), so a
- * second {@link FileDocProvider} on the same socket — e.g. the chat preview alongside the Files editor —
- * also receives the mid-stream ops. The emitting provider no-ops on its own echo (the ops are already
- * applied locally), so broadcasting back to the sender is harmless.
+ * client edit, it is marked so peers do not treat it as a durable user edit. The emitting provider no-ops
+ * on its own echo because the operations are already applied locally.
*/
const AGENT_SYNC_ORIGIN = Symbol('file-doc-agent-sync')
@@ -783,7 +785,7 @@ async function mergeMarkdownIntoRoom(
/**
* Get (or lazily create) the authoritative document for a room, wiring the two
* relay handlers exactly once: document updates and awareness changes are
- * broadcast to the room, excluding the origin socket (it already applied them).
+ * broadcast to the room.
*/
function getOrCreateRoom(io: Server, ref: RoomRef): FileDocRoom {
const name = roomName(ref)
@@ -821,18 +823,12 @@ function getOrCreateRoom(io: Server, ref: RoomRef): FileDocRoom {
const encoder = encoding.createEncoder()
encoding.writeVarUint(encoder, FILE_DOC_MESSAGE_TYPE.SYNC)
syncProtocol.writeUpdate(encoder, update)
- // Fan out to THIS task's clients only (excluding the origin socket if local — a user edit OR an
- // agent-streamed frame). Cross-task delivery rides the shared stream — every task's tailer applies +
- // runs its own local fan-out.
- // A client edit excludes its own sender socket (echo suppression). An agent frame broadcasts to the
- // WHOLE room — no socket excluded — so a same-socket sibling provider (chat preview + Files editor)
- // stays live mid-stream; the emitting provider no-ops on its own echo.
- broadcastLocal(
- io,
- name,
- encoding.toUint8Array(encoder),
- origin === AGENT_SYNC_ORIGIN ? null : originSocketId(origin)
- )
+ // Fan out to every client on THIS task, including the origin socket. One shared Socket.IO connection
+ // can host multiple providers for this file; excluding the whole socket would strand the sibling
+ // provider's distinct Y.Doc. Yjs updates are idempotent, and the originating provider applies its
+ // echo with the provider as transaction origin, so it does not send the update again. Cross-task
+ // delivery rides the shared stream, where every task's tailer runs its own local fan-out.
+ broadcastLocal(io, name, encoding.toUint8Array(encoder), null)
// Share every locally-originated update to the stream so peers converge. Skip updates that already
// came FROM the stream (REDIS_ORIGIN / REDIS_SNAPSHOT_ORIGIN / REDIS_AGENT_ORIGIN) and SEED_ORIGIN —
// the seed is published EXPLICITLY and AWAITED under the seed lock (so it lands before the lock
@@ -898,12 +894,15 @@ function getOrCreateRoom(io: Server, ref: RoomRef): FileDocRoom {
function emitJoinError(
socket: AuthenticatedSocket,
fileId: unknown,
+ clientId: unknown,
error: string,
code: string,
retryable: boolean
) {
+ const normalizedClientId = isYjsClientId(clientId) ? clientId : undefined
socket.emit(FILE_DOC_EVENTS.JOIN_ERROR, {
fileId: typeof fileId === 'string' ? fileId : '',
+ clientId: normalizedClientId,
error,
code,
retryable,
@@ -1113,30 +1112,47 @@ export function setupWorkspaceFileDocHandlers(
const userName = socket.userName
if (!userId || !userName) {
- emitJoinError(socket, fileId, 'Authentication required', 'AUTHENTICATION_REQUIRED', false)
+ emitJoinError(
+ socket,
+ fileId,
+ clientId,
+ 'Authentication required',
+ 'AUTHENTICATION_REQUIRED',
+ false
+ )
return
}
if (!roomManager.isReady()) {
- emitJoinError(socket, fileId, 'Realtime unavailable', 'ROOM_MANAGER_UNAVAILABLE', true)
+ emitJoinError(
+ socket,
+ fileId,
+ clientId,
+ 'Realtime unavailable',
+ 'ROOM_MANAGER_UNAVAILABLE',
+ true
+ )
return
}
if (
typeof fileId !== 'string' ||
fileId.length === 0 ||
- // A Yjs clientID is a uint32; reject NaN/Infinity/negative/non-integer so a malformed id
- // can't become a bogus ownership key.
- !Number.isInteger(clientId) ||
- clientId < 0
+ // A Yjs clientID is a uint32; reject malformed values before they can become ownership keys.
+ !isYjsClientId(clientId)
) {
- emitJoinError(socket, fileId, 'Invalid join payload', 'INVALID_PAYLOAD', false)
+ emitJoinError(socket, fileId, clientId, 'Invalid join payload', 'INVALID_PAYLOAD', false)
return
}
- // Claim this JOIN's generation before the async authorize below, and record the file the
- // socket now intends to edit so a leave for it can cancel this join if it's still in-flight.
- generation = (joinGeneration.get(socket.id) ?? 0) + 1
- joinGeneration.set(socket.id, generation)
- currentFileId = fileId
+ // A generation represents the socket's intended FILE, not an individual provider. Co-mounted
+ // providers for the same file must be allowed to join concurrently; switching files advances the
+ // generation so every in-flight join for the old file is cancelled together.
+ if (currentFileId !== fileId) {
+ generation = (joinGeneration.get(socket.id) ?? 0) + 1
+ joinGeneration.set(socket.id, generation)
+ currentFileId = fileId
+ } else {
+ generation = joinGeneration.get(socket.id) ?? 0
+ }
const room = fileDocRoom(fileId)
const name = roomName(room)
@@ -1153,7 +1169,7 @@ export function setupWorkspaceFileDocHandlers(
accessDenied: 'Access denied to file',
},
emitError: ({ error, code, retryable }) =>
- emitJoinError(socket, fileId, error, code, retryable),
+ emitJoinError(socket, fileId, clientId, error, code, retryable),
})
if (!authorized) return
@@ -1190,7 +1206,7 @@ export function setupWorkspaceFileDocHandlers(
logger.warn(
`User ${userId} lost write access to file ${fileId} before the join completed`
)
- emitJoinError(socket, fileId, 'Access denied to file', 'ACCESS_DENIED', false)
+ emitJoinError(socket, fileId, clientId, 'Access denied to file', 'ACCESS_DENIED', false)
return
}
@@ -1219,7 +1235,14 @@ export function setupWorkspaceFileDocHandlers(
const owner = clientMap.get(clientId)
if (owner === undefined) continue
if (owner.userId !== userId) {
- emitJoinError(socket, fileId, 'Client id already in use', 'CLIENT_ID_IN_USE', false)
+ emitJoinError(
+ socket,
+ fileId,
+ clientId,
+ 'Client id already in use',
+ 'CLIENT_ID_IN_USE',
+ false
+ )
return
}
// Same user reclaiming its client id on a stale prior socket: evict just THAT clientID's
@@ -1268,7 +1291,11 @@ export function setupWorkspaceFileDocHandlers(
// Name the document this room holds, so a client that still carries a DIFFERENT one (its room
// outlived by a document rebuilt in its place) can refuse to merge instead of unioning two
// documents into the file twice over. Read after readiness — before it, the room has no doc yet.
- socket.emit(FILE_DOC_EVENTS.JOIN_SUCCESS, { fileId, docId: docIdOf(entry.doc) })
+ socket.emit(FILE_DOC_EVENTS.JOIN_SUCCESS, {
+ fileId,
+ clientId,
+ docId: docIdOf(entry.doc),
+ })
// Server-authenticated roster → everyone in the room, including this joiner.
broadcastFileDocPresence(io, name, entry)
@@ -1321,7 +1348,7 @@ export function setupWorkspaceFileDocHandlers(
(generation !== undefined && joinGeneration.get(socket.id) !== generation)
)
return
- emitJoinError(socket, fileId, 'Failed to join file document', 'JOIN_FAILED', true)
+ emitJoinError(socket, fileId, clientId, 'Failed to join file document', 'JOIN_FAILED', true)
}
})
diff --git a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.test.tsx b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.test.tsx
index 2f0711f0091..4416a77ee83 100644
--- a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.test.tsx
+++ b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.test.tsx
@@ -5,21 +5,30 @@ import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
-const { mockCal, mockCalComponent, mockConsent, mockGetCalApi, mockTrackGoogleEvent } = vi.hoisted(
- () => ({
- mockCal: vi.fn(),
- mockCalComponent: vi.fn(() => null),
- mockConsent: { marketing: true, measurement: true },
- mockGetCalApi: vi.fn(),
- mockTrackGoogleEvent: vi.fn(),
- })
-)
+const {
+ mockCal,
+ mockCalComponent,
+ mockConsent,
+ mockGetCalApi,
+ mockTrackGoogleAdsConversion,
+ mockTrackGoogleEvent,
+} = vi.hoisted(() => ({
+ mockCal: vi.fn(),
+ mockCalComponent: vi.fn(() => null),
+ mockConsent: { marketing: true, measurement: true },
+ mockGetCalApi: vi.fn(),
+ mockTrackGoogleAdsConversion: vi.fn(),
+ mockTrackGoogleEvent: vi.fn(),
+}))
vi.mock('@calcom/embed-react', () => ({
default: mockCalComponent,
getCalApi: mockGetCalApi,
}))
-vi.mock('@/lib/analytics/google', () => ({ trackGoogleEvent: mockTrackGoogleEvent }))
+vi.mock('@/lib/analytics/google', () => ({
+ trackGoogleAdsConversion: mockTrackGoogleAdsConversion,
+ trackGoogleEvent: mockTrackGoogleEvent,
+}))
vi.mock('@/lib/consent/scripts', () => ({ X_DEMO_BOOKED_EVENT_ID: 'demo-booked' }))
vi.mock('@/lib/consent/tracking-consent', () => ({
useTrackingConsent: () => mockConsent,
@@ -37,6 +46,18 @@ const LEAD = {
notes: 'Company: Analytical Engines\nTopic: Demo',
}
+interface BookingRegistration {
+ action: string
+ callback: () => void
+}
+
+/** The listener the scheduler registered with the Cal.com embed, if any. */
+function bookingRegistration(): BookingRegistration | undefined {
+ return mockCal.mock.calls.find(([method]) => method === 'on')?.[1] as
+ | BookingRegistration
+ | undefined
+}
+
describe('DemoScheduler', () => {
let container: HTMLDivElement
let root: Root
@@ -101,9 +122,7 @@ describe('DemoScheduler', () => {
await Promise.resolve()
})
- const registration = mockCal.mock.calls.find(([method]) => method === 'on')?.[1] as
- | { action: string; callback: () => void }
- | undefined
+ const registration = bookingRegistration()
expect(registration?.action).toBe('bookingSuccessfulV2')
registration?.callback()
@@ -112,6 +131,7 @@ describe('DemoScheduler', () => {
form_name: 'sim_demo',
booking_status: 'scheduled',
})
+ expect(mockTrackGoogleAdsConversion).toHaveBeenCalledWith('demo_booked')
expect(trackXEvent).toHaveBeenCalledWith('event', 'demo-booked', {})
await act(async () => {
@@ -125,6 +145,23 @@ describe('DemoScheduler', () => {
root = createRoot(container)
})
+ it('sends measurement analytics but no ad conversion without marketing consent', async () => {
+ mockConsent.marketing = false
+ const trackXEvent = vi.fn()
+ window.twq = trackXEvent
+
+ await act(async () => {
+ root.render( )
+ await Promise.resolve()
+ })
+
+ bookingRegistration()?.callback()
+
+ expect(mockTrackGoogleEvent).toHaveBeenCalledOnce()
+ expect(mockTrackGoogleAdsConversion).not.toHaveBeenCalled()
+ expect(trackXEvent).not.toHaveBeenCalled()
+ })
+
it('does not register booking analytics without measurement or marketing consent', async () => {
mockConsent.marketing = false
mockConsent.measurement = false
@@ -138,7 +175,7 @@ describe('DemoScheduler', () => {
hideEventTypeDetails: true,
styles: { branding: { brandColor: '#6f3dfa' } },
})
- expect(mockCal.mock.calls.some(([method]) => method === 'on')).toBe(false)
+ expect(bookingRegistration()).toBeUndefined()
})
it('preloads the configured booker only once', async () => {
diff --git a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx
index 682c1990987..5363069745c 100644
--- a/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx
+++ b/apps/sim/app/(landing)/demo/components/demo-scheduler/demo-scheduler.tsx
@@ -2,7 +2,7 @@
import { useEffect } from 'react'
import Cal, { getCalApi } from '@calcom/embed-react'
-import { trackGoogleEvent } from '@/lib/analytics/google'
+import { trackGoogleAdsConversion, trackGoogleEvent } from '@/lib/analytics/google'
import { X_DEMO_BOOKED_EVENT_ID } from '@/lib/consent/scripts'
import { useTrackingConsent } from '@/lib/consent/tracking-consent'
import type { DemoLead } from '@/app/(landing)/demo/components/demo-form'
@@ -102,7 +102,10 @@ export function DemoScheduler({ lead }: DemoSchedulerProps) {
booking_status: 'scheduled',
})
}
- if (marketing) window.twq?.('event', X_DEMO_BOOKED_EVENT_ID, {})
+ if (marketing) {
+ trackGoogleAdsConversion('demo_booked')
+ window.twq?.('event', X_DEMO_BOOKED_EVENT_ID, {})
+ }
}
const api = getCalApi({ namespace: CAL_NAMESPACE, embedJsUrl: CAL_EMBED.embedJsUrl })
api
diff --git a/apps/sim/app/(landing)/models/(shell)/[provider]/[model]/page.tsx b/apps/sim/app/(landing)/models/(shell)/[provider]/[model]/page.tsx
index 3c59189fc90..a44bd563c4e 100644
--- a/apps/sim/app/(landing)/models/(shell)/[provider]/[model]/page.tsx
+++ b/apps/sim/app/(landing)/models/(shell)/[provider]/[model]/page.tsx
@@ -1,3 +1,4 @@
+import { Fragment } from 'react'
import { ChipLink } from '@sim/emcn'
import type { Metadata } from 'next'
import { notFound } from 'next/navigation'
@@ -200,6 +201,28 @@ export default async function ModelPage({
}
/>
+ {model.pricing.tiers?.map((tier) => {
+ const threshold = formatTokenCount(tier.aboveInputTokens)
+
+ return (
+
+ ${threshold})`}
+ value={`${formatPrice(tier.input)}/1M`}
+ />
+ ${threshold})`}
+ value={
+ tier.cachedInput !== undefined ? `${formatPrice(tier.cachedInput)}/1M` : 'N/A'
+ }
+ />
+ ${threshold})`}
+ value={`${formatPrice(tier.output)}/1M`}
+ />
+
+ )
+ })}
> = (() => {
const map: Record> = {}
@@ -24,6 +27,14 @@ const PROVIDER_ICON_MAP: Record> =
return map
})()
+function getRoundedRightBarPath(x: number, width: number): string {
+ const right = x + width
+ const radiusX = Math.min(CHART_BAR_END_RADIUS_X, width / 2)
+ const radiusY = CHART_BAR_END_RADIUS_Y
+
+ return `M ${x} 0 H ${right - radiusX} Q ${right} 0 ${right} ${radiusY} V ${CHART_BAR_HEIGHT - radiusY} Q ${right} ${CHART_BAR_HEIGHT} ${right - radiusX} ${CHART_BAR_HEIGHT} H ${x} Z`
+}
+
function selectComparisonModels(models: CatalogModel[]): CatalogModel[] {
const seen = new Set()
const result: CatalogModel[] = []
@@ -96,7 +107,7 @@ function StackedCostChart({ models }: ChartProps) {
Cost
- Per 1M tokens
+ Standard short-context rates per 1M tokens
@@ -104,6 +115,9 @@ function StackedCostChart({ models }: ChartProps) {
{data.entries.map(({ model, input, output, total }) => {
const totalPct = data.maxTotal > 0 ? (total / data.maxTotal) * 100 : 0
const inputPct = total > 0 ? (input / total) * 100 : 0
+ const plottedTotalPct = Math.max(totalPct, 3)
+ const plottedInputPct = (plottedTotalPct * inputPct) / 100
+ const plottedOutputPct = plottedTotalPct - plottedInputPct
const color = getProviderColor(model.providerId)
return (
@@ -113,29 +127,34 @@ function StackedCostChart({ models }: ChartProps) {
className='-mx-2 flex items-center gap-3 rounded-md px-2 transition-colors hover:bg-[var(--surface-hover)]'
>