feat(square): add Square integration with 34 commerce operations (#5053)

* feat(square): add Square integration with 34 commerce operations

Add a Square integration (API-key auth via personal access token) covering
payments, refunds, customers, locations, orders, invoices, catalog, and
inventory. Catalog image upload routes through an internal API endpoint using
the shared UserFile handling pattern. Adds a dedicated square-errors extractor.

* fix(square): correct catalog image part name and address review feedback

- Fix catalog image upload: Square's multipart part for the binary is `file`,
  not `image_file` (per the live API cURL examples); this would have caused
  upload failures
- Catalog image route: check response.ok before parsing, drop the unreachable
  legacy base64 path, derive MIME from the uploaded file
- Block: split the search query field per operation so placeholders match each
  endpoint's schema; parse each JSON field individually so errors name the field
- Round out coverage: complete_payment version_token; customer nickname/birthday;
  batch inventory states/updated_after/limit

* fix(square): correct canonical file param usage and revert query split

- Read the catalog image file from the canonical `params.file` (the basic/advanced
  inputs are collapsed before the params function runs) instead of the raw
  uploadFile/fileRef ids, which no longer exist at that point — fixes the
  Canonical Param Validation test and a latent upload bug
- Revert the per-operation query split: canonicalParamId is only valid for
  basic/advanced pairs under one condition. Use a single query field with a
  schema-neutral placeholder and a wand prompt that covers each search operation

* chore(square): trigger fresh review

* fix(square): single-location invoice search and guard numeric coercion

- SearchInvoices: Square's invoice filter accepts only one location, so take a
  single locationId (string) instead of an array and wrap it as
  query.filter.location_ids: [locationId]
- Block: fail locally with a clear "<field> must be a valid number" error when
  amount/limit/version/orderVersion are non-numeric instead of forwarding NaN

* fix(square): accept real booleans for autocomplete/includeRelatedObjects

Coerce these from both the dropdown's string values and actual booleans
(which can arrive via connected blocks or templated inputs), so true is not
silently flipped to false.

* fix(square): validate parsed JSON field shapes (array vs object)

parseJsonField now enforces the expected shape so a valid-but-wrong-type value
(e.g. a JSON string where an array is expected for locationIds/objectTypes/
paymentIds/catalogObjectIds/states, or a non-object for order/invoice/etc.)
fails locally with a clear message instead of a confusing Square API error.
This commit is contained in:
Waleed
2026-06-15 09:55:14 -07:00
committed by GitHub
parent 06191a7fb9
commit 940506ad09
51 changed files with 6821 additions and 2 deletions
+11
View File
@@ -1958,6 +1958,17 @@ export function WhatsAppIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function SquareIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 1999.98 501.42' xmlns='http://www.w3.org/2000/svg'>
<path
fill='#fff'
d='M501.42,83.79v333.84c0,46.27-37.5,83.79-83.79,83.79H83.79c-46.28,0-83.79-37.5-83.79-83.79V83.79C0,37.51,37.52,0,83.79,0h333.84c46.29,0,83.79,37.5,83.79,83.79h0ZM410.22,117.64c0-14.61-11.85-26.45-26.45-26.45H117.62c-14.61,0-26.45,11.84-26.45,26.45v266.19c0,14.61,11.84,26.45,26.45,26.45h266.17c14.61,0,26.45-11.85,26.45-26.45V117.64h-.02ZM182.31,197.59c0-8.43,6.79-15.26,15.17-15.26h106.4c8.39,0,15.17,6.84,15.17,15.26v106.24c0,8.43-6.75,15.26-15.17,15.26h-106.4c-8.39,0-15.17-6.84-15.17-15.26v-106.24ZM778.93,221.94l-3.85-.86c-41.04-9.31-65.81-14.93-65.81-42,0-24.2,23.02-41.11,55.98-41.11,30.52,0,53.74,12.76,73.08,40.16,1.11,1.57,2.84,2.61,4.74,2.84,1.89.23,3.79-.35,5.23-1.59l32.16-27.71c2.68-2.31,3.15-6.22,1.1-9.09-24.19-33.89-67.01-54.12-114.56-54.12-31.56,0-60.34,9.26-81.04,26.08-21.73,17.65-33.21,41.93-33.21,70.23,0,63.76,54.74,76.94,98.71,87.53,4.45,1.08,8.77,2.1,12.95,3.08,39.74,9.36,66,15.54,66,43.74s-24.04,45.48-61.24,45.48c-33.71,0-64.35-17.1-86.28-48.14-1.1-1.55-2.8-2.59-4.68-2.84s-3.73.28-5.2,1.49l-33.86,27.99c-2.72,2.25-3.28,6.14-1.3,9.05,25.63,37.64,76.48,61.97,129.56,61.97,32.56,0,62.52-9.57,84.36-26.95,23.27-18.51,35.57-44.01,35.57-73.73,0-67.27-57.62-80.13-108.45-91.48l.04-.02ZM1126.33,177.73h-40.76c-3.74,0-6.78,3.04-6.78,6.78v19.06c-12.6-14.21-33.77-30.22-65.18-30.22s-56.88,12.32-75.37,35.62c-17.16,21.63-26.62,51.73-26.62,84.75s9.45,63.12,26.62,84.75c18.49,23.31,44.56,35.62,75.37,35.62,26.63,0,49.1-9.45,65.18-27.37v107.92c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78V184.51c0-3.74-3.04-6.78-6.78-6.78ZM1080.1,287.16v13.57c0,39.86-21.97,65.61-55.98,65.61-36.15,0-57.74-27.15-57.74-72.61s21.58-72.61,57.74-72.61c34.01,0,55.98,25.93,55.98,66.05h0ZM1360.59,177.73h-40.76c-3.74,0-6.78,3.04-6.78,6.78v130.66c0,32.45-23.32,49.42-46.36,49.42-26.03,0-39.79-15.58-39.79-45.04v-135.03c0-3.74-3.04-6.78-6.78-6.78h-40.76c-3.74,0-6.78,3.04-6.78,6.78v146.41c0,50.53,30.93,83.17,78.8,83.17,23.76,0,43.95-9.67,61.67-29.56v17.96c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78v-217.99c0-3.74-3.04-6.78-6.78-6.78h0ZM1607.92,367.06c-8.79-.24-12.72-4.78-12.72-14.7v-98.9c0-51.66-31.71-80.11-89.3-80.11-44.71,0-80.07,23.24-94.59,62.18-.66,1.78-.52,3.78.39,5.47.93,1.73,2.57,2.98,4.48,3.42l37.83,8.71c3.37.77,6.78-1.14,7.94-4.45,6.74-19.11,20.01-28.01,41.76-28.01,25.53,0,38.48,11.62,38.48,34.54v3.2l-62.73,12.98c-53.04,11.03-77.74,34.25-77.74,73.09s32.22,68.73,78.36,68.73c28.25,0,51.2-8.69,66.46-25.16,9.13,17.73,33.93,26.29,62.3,21.35,3.24-.57,5.6-3.38,5.6-6.69v-28.9c0-3.63-2.93-6.67-6.52-6.77v.02ZM1542.2,300.53v26.89c0,27.56-27.28,41.98-54.24,41.98-20.26,0-32.35-10.13-32.35-27.1,0-19.37,14.63-26.41,38.23-31.5l48.36-10.27ZM1774.34,177.32c-3.17-.52-6.47-.77-10.09-.77-27.5,0-50.92,12.35-62.11,32.47v-24.51c0-3.74-3.04-6.78-6.78-6.78h-40.76c-3.74,0-6.78,3.04-6.78,6.78v217.99c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78v-114.9c0-34.27,23.2-57.3,57.74-57.3,4.7,0,8.63.17,12.74.56,1.89.18,3.79-.45,5.2-1.73s2.22-3.11,2.22-5.02v-40.09c0-3.34-2.39-6.16-5.69-6.7h-.01ZM1973.74,206.72c-18.32-21.83-44.82-33.36-76.62-33.36s-59.09,12.34-79.33,34.76c-19.98,22.12-30.98,52.52-30.98,85.61,0,70.87,46.26,120.37,112.49,120.37,43.9,0,79.17-21.53,96.77-59.08.8-1.7.85-3.61.14-5.37-.71-1.76-2.14-3.15-3.91-3.82l-33.69-12.76c-3.39-1.28-7.27.37-8.63,3.68-8.08,19.63-26.56,30.9-50.68,30.9-33.08,0-56.1-23.59-60.26-61.64h154.16c3.74,0,6.78-3.04,6.78-6.78v-11.63c0-31.99-9.32-60.72-26.25-80.88h.01ZM1945.21,264.82h-103.37c7.73-28.91,27.66-45.45,54.84-45.45,34.72,0,47.8,23.3,48.52,45.45h.01Z'
/>
</svg>
)
}
export function StripeIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
+2
View File
@@ -191,6 +191,7 @@ import {
SlackIcon,
SmtpIcon,
SQSIcon,
SquareIcon,
SshIcon,
STSIcon,
STTIcon,
@@ -439,6 +440,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
slack: SlackIcon,
smtp: SmtpIcon,
sqs: SQSIcon,
square: SquareIcon,
ssh: SshIcon,
stagehand: StagehandIcon,
stripe: StripeIcon,
@@ -192,6 +192,7 @@
"slack",
"smtp",
"sqs",
"square",
"ssh",
"stagehand",
"stripe",
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { generateId } from '@sim/utils/id'
import { type NextRequest, NextResponse } from 'next/server'
import { squareCatalogImageContract } from '@/lib/api/contracts/tools/square'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { processFilesToUserFiles, type RawFileInput } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import { assertToolFileAccess } from '@/app/api/files/authorization'
import { SQUARE_API_VERSION, SQUARE_BASE_URL } from '@/tools/square/types'
export const dynamic = 'force-dynamic'
const logger = createLogger('SquareCatalogImageAPI')
export const POST = withRouteHandler(async (request: NextRequest) => {
const requestId = generateRequestId()
try {
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success || !authResult.userId) {
logger.warn(`[${requestId}] Unauthorized Square catalog image upload: ${authResult.error}`)
return NextResponse.json(
{ success: false, error: authResult.error || 'Authentication required' },
{ status: 401 }
)
}
const parsed = await parseRequest(squareCatalogImageContract, request, {})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
if (!validatedData.file) {
return NextResponse.json({ success: false, error: 'File is required' }, { status: 400 })
}
const userFiles = processFilesToUserFiles(
[validatedData.file as RawFileInput],
requestId,
logger
)
if (userFiles.length === 0) {
return NextResponse.json({ success: false, error: 'Invalid file input' }, { status: 400 })
}
const userFile = userFiles[0]
const denied = await assertToolFileAccess(userFile.key, authResult.userId, requestId, logger)
if (denied) return denied
const fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
const fileName = validatedData.fileName || userFile.name
const mimeType = userFile.type || 'application/octet-stream'
const imageRequest: Record<string, unknown> = {
idempotency_key: validatedData.idempotencyKey || generateId(),
image: {
type: 'IMAGE',
id: '#square_catalog_image',
image_data: validatedData.caption ? { caption: validatedData.caption } : {},
},
}
if (validatedData.objectId) imageRequest.object_id = validatedData.objectId
const formData = new FormData()
formData.append('request', JSON.stringify(imageRequest))
formData.append('file', new Blob([new Uint8Array(fileBuffer)], { type: mimeType }), fileName)
const response = await fetch(`${SQUARE_BASE_URL}/v2/catalog/images`, {
method: 'POST',
headers: {
Authorization: `Bearer ${validatedData.accessToken}`,
'Square-Version': SQUARE_API_VERSION,
},
body: formData,
})
if (!response.ok) {
const errorText = await response.text()
let detail: string | undefined
try {
detail = JSON.parse(errorText)?.errors?.[0]?.detail
} catch {
detail = undefined
}
logger.error(`[${requestId}] Square API error:`, { status: response.status, body: errorText })
return NextResponse.json(
{
success: false,
error: detail || `Failed to upload catalog image (HTTP ${response.status})`,
},
{ status: response.status }
)
}
const data = await response.json()
const object = data.image ?? {}
return NextResponse.json({
success: true,
output: {
object,
metadata: {
id: object.id ?? '',
type: object.type ?? null,
version: object.version ?? null,
},
},
})
} catch (error) {
logger.error(`[${requestId}] Unexpected error:`, error)
return NextResponse.json(
{ success: false, error: getErrorMessage(error, 'Unknown error') },
{ status: 500 }
)
}
})
+978
View File
@@ -0,0 +1,978 @@
import { SquareIcon } from '@/components/icons'
import type { BlockConfig, BlockMeta } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
import { normalizeFileInput } from '@/blocks/utils'
import type { SquareResponse } from '@/tools/square/types'
export const SquareBlock: BlockConfig<SquareResponse> = {
type: 'square',
name: 'Square',
description: 'Process payments and manage Square commerce data',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate Square into the workflow. Take and refund payments, manage customers, build catalog items and images, create and search orders, and issue invoices. Authenticate with a Square access token (personal access token).',
docsLink: 'https://docs.sim.ai/integrations/square',
category: 'tools',
integrationType: IntegrationType.Commerce,
bgColor: '#000000',
icon: SquareIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
// Payments
{ label: 'Create Payment', id: 'create_payment' },
{ label: 'Get Payment', id: 'get_payment' },
{ label: 'List Payments', id: 'list_payments' },
{ label: 'Cancel Payment', id: 'cancel_payment' },
{ label: 'Complete Payment', id: 'complete_payment' },
// Refunds
{ label: 'Refund Payment', id: 'refund_payment' },
{ label: 'Get Refund', id: 'get_refund' },
{ label: 'List Refunds', id: 'list_refunds' },
// Customers
{ label: 'Create Customer', id: 'create_customer' },
{ label: 'Get Customer', id: 'get_customer' },
{ label: 'List Customers', id: 'list_customers' },
{ label: 'Search Customers', id: 'search_customers' },
{ label: 'Update Customer', id: 'update_customer' },
{ label: 'Delete Customer', id: 'delete_customer' },
// Locations
{ label: 'List Locations', id: 'list_locations' },
{ label: 'Get Location', id: 'get_location' },
// Orders
{ label: 'Create Order', id: 'create_order' },
{ label: 'Get Order', id: 'get_order' },
{ label: 'Search Orders', id: 'search_orders' },
{ label: 'Pay Order', id: 'pay_order' },
// Invoices
{ label: 'Create Invoice', id: 'create_invoice' },
{ label: 'Get Invoice', id: 'get_invoice' },
{ label: 'List Invoices', id: 'list_invoices' },
{ label: 'Search Invoices', id: 'search_invoices' },
{ label: 'Publish Invoice', id: 'publish_invoice' },
{ label: 'Cancel Invoice', id: 'cancel_invoice' },
{ label: 'Delete Invoice', id: 'delete_invoice' },
// Catalog
{ label: 'Upsert Catalog Object', id: 'upsert_catalog_object' },
{ label: 'Get Catalog Object', id: 'get_catalog_object' },
{ label: 'List Catalog', id: 'list_catalog' },
{ label: 'Search Catalog Objects', id: 'search_catalog_objects' },
{ label: 'Create Catalog Image', id: 'create_catalog_image' },
{ label: 'Delete Catalog Object', id: 'delete_catalog_object' },
// Inventory
{ label: 'Batch Retrieve Inventory Counts', id: 'batch_retrieve_inventory_counts' },
],
value: () => 'create_payment',
},
{
id: 'apiKey',
title: 'Square Access Token',
type: 'short-input',
password: true,
placeholder: 'Enter your Square access token',
required: true,
},
// Payments
{
id: 'sourceId',
title: 'Source ID',
type: 'short-input',
placeholder: 'Card nonce, card-on-file ID, or wallet token',
condition: { field: 'operation', value: 'create_payment' },
required: { field: 'operation', value: 'create_payment' },
},
{
id: 'paymentId',
title: 'Payment ID',
type: 'short-input',
placeholder: 'Square payment ID',
condition: {
field: 'operation',
value: ['get_payment', 'refund_payment', 'cancel_payment', 'complete_payment'],
},
required: {
field: 'operation',
value: ['get_payment', 'refund_payment', 'cancel_payment', 'complete_payment'],
},
},
{
id: 'refundId',
title: 'Refund ID',
type: 'short-input',
placeholder: 'Square refund ID',
condition: { field: 'operation', value: 'get_refund' },
required: { field: 'operation', value: 'get_refund' },
},
{
id: 'status',
title: 'Status',
type: 'short-input',
placeholder: 'Filter by status (e.g. COMPLETED)',
condition: { field: 'operation', value: 'list_refunds' },
mode: 'advanced',
},
{
id: 'amount',
title: 'Amount',
type: 'short-input',
placeholder: 'Smallest denomination (e.g. 1000 for $10.00)',
condition: { field: 'operation', value: ['create_payment', 'refund_payment'] },
required: { field: 'operation', value: ['create_payment', 'refund_payment'] },
},
{
id: 'currency',
title: 'Currency',
type: 'short-input',
placeholder: 'ISO 4217 code (e.g. USD)',
condition: { field: 'operation', value: ['create_payment', 'refund_payment'] },
required: { field: 'operation', value: ['create_payment', 'refund_payment'] },
},
{
id: 'reason',
title: 'Refund Reason',
type: 'short-input',
placeholder: 'Reason for the refund',
condition: { field: 'operation', value: 'refund_payment' },
},
{
id: 'versionToken',
title: 'Version Token',
type: 'short-input',
placeholder: 'Optional version token for concurrency control',
condition: { field: 'operation', value: 'complete_payment' },
mode: 'advanced',
},
{
id: 'autocomplete',
title: 'Capture Immediately',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: { field: 'operation', value: 'create_payment' },
mode: 'advanced',
},
{
id: 'beginTime',
title: 'Begin Time',
type: 'short-input',
placeholder: 'RFC 3339 timestamp',
condition: { field: 'operation', value: ['list_payments', 'list_refunds'] },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an RFC 3339 timestamp. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
},
{
id: 'endTime',
title: 'End Time',
type: 'short-input',
placeholder: 'RFC 3339 timestamp',
condition: { field: 'operation', value: ['list_payments', 'list_refunds'] },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an RFC 3339 timestamp. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
},
// Customers
{
id: 'customerId',
title: 'Customer ID',
type: 'short-input',
placeholder: 'Square customer ID',
condition: {
field: 'operation',
value: ['create_payment', 'get_customer', 'update_customer', 'delete_customer'],
},
required: {
field: 'operation',
value: ['get_customer', 'update_customer', 'delete_customer'],
},
},
{
id: 'givenName',
title: 'First Name',
type: 'short-input',
placeholder: 'Customer first name',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
},
{
id: 'familyName',
title: 'Last Name',
type: 'short-input',
placeholder: 'Customer last name',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
},
{
id: 'companyName',
title: 'Company Name',
type: 'short-input',
placeholder: 'Customer business name',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
mode: 'advanced',
},
{
id: 'nickname',
title: 'Nickname',
type: 'short-input',
placeholder: 'Customer nickname',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
mode: 'advanced',
},
{
id: 'emailAddress',
title: 'Email Address',
type: 'short-input',
placeholder: 'customer@example.com',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
},
{
id: 'phoneNumber',
title: 'Phone Number',
type: 'short-input',
placeholder: '+15551234567',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
mode: 'advanced',
},
{
id: 'birthday',
title: 'Birthday',
type: 'short-input',
placeholder: 'YYYY-MM-DD or MM-DD',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
mode: 'advanced',
},
{
id: 'address',
title: 'Address (JSON)',
type: 'code',
language: 'json',
placeholder: '{"address_line_1": "123 Main St", "locality": "New York", "country": "US"}',
condition: { field: 'operation', value: ['create_customer', 'update_customer'] },
mode: 'advanced',
},
{
id: 'sortField',
title: 'Sort Field',
type: 'dropdown',
options: [
{ label: 'Default', id: 'DEFAULT' },
{ label: 'Created At', id: 'CREATED_AT' },
],
condition: { field: 'operation', value: 'list_customers' },
mode: 'advanced',
},
{
id: 'sortOrder',
title: 'Sort Order',
type: 'dropdown',
options: [
{ label: 'Ascending', id: 'ASC' },
{ label: 'Descending', id: 'DESC' },
],
condition: { field: 'operation', value: 'list_customers' },
mode: 'advanced',
},
// Shared note / reference (payments + customers)
{
id: 'note',
title: 'Note',
type: 'long-input',
placeholder: 'Optional note',
condition: {
field: 'operation',
value: ['create_payment', 'create_customer', 'update_customer'],
},
mode: 'advanced',
},
{
id: 'referenceId',
title: 'Reference ID',
type: 'short-input',
placeholder: 'Optional external reference',
condition: {
field: 'operation',
value: ['create_payment', 'create_customer', 'update_customer'],
},
mode: 'advanced',
},
// Search query — shared across the three search operations. The placeholder
// stays schema-neutral (each endpoint expects a different query shape) and
// the examples for each are spelled out in the wand prompt.
{
id: 'query',
title: 'Query (JSON)',
type: 'code',
language: 'json',
placeholder: 'Square search query JSON for the selected operation',
condition: {
field: 'operation',
value: ['search_customers', 'search_orders', 'search_catalog_objects'],
},
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate a Square search query JSON object for the selected operation. For Search Customers use a filter like {"filter":{"email_address":{"exact":"a@b.com"}}}; for Search Orders use {"filter":{"state_filter":{"states":["OPEN"]}}}; for Search Catalog Objects use {"text_query":{"keywords":["coffee"]}}. Return ONLY the JSON object.',
generationType: 'json-object',
},
},
// Orders
{
id: 'order',
title: 'Order (JSON)',
type: 'code',
language: 'json',
placeholder:
'{"location_id": "L123", "line_items": [{"name": "Coffee", "quantity": "1", "base_price_money": {"amount": 250, "currency": "USD"}}]}',
condition: { field: 'operation', value: 'create_order' },
required: { field: 'operation', value: 'create_order' },
wandConfig: {
enabled: true,
prompt:
'Generate a Square order JSON object with a location_id and line_items. Return ONLY the JSON object.',
generationType: 'json-object',
},
},
{
id: 'orderId',
title: 'Order ID',
type: 'short-input',
placeholder: 'Square order ID',
condition: { field: 'operation', value: ['create_payment', 'get_order', 'pay_order'] },
required: { field: 'operation', value: ['get_order', 'pay_order'] },
},
{
id: 'paymentIds',
title: 'Payment IDs (JSON Array)',
type: 'code',
language: 'json',
placeholder: '["paymentId1", "paymentId2"]',
condition: { field: 'operation', value: 'pay_order' },
mode: 'advanced',
},
{
id: 'orderVersion',
title: 'Order Version',
type: 'short-input',
placeholder: 'Current order version',
condition: { field: 'operation', value: 'pay_order' },
mode: 'advanced',
},
{
id: 'locationIds',
title: 'Location IDs (JSON Array)',
type: 'code',
language: 'json',
placeholder: '["L123", "L456"]',
condition: {
field: 'operation',
value: ['search_orders', 'batch_retrieve_inventory_counts'],
},
required: { field: 'operation', value: 'search_orders' },
},
// Invoices
{
id: 'invoice',
title: 'Invoice (JSON)',
type: 'code',
language: 'json',
placeholder:
'{"location_id": "L123", "order_id": "O123", "primary_recipient": {"customer_id": "C123"}, "payment_requests": [{"request_type": "BALANCE"}]}',
condition: { field: 'operation', value: 'create_invoice' },
required: { field: 'operation', value: 'create_invoice' },
wandConfig: {
enabled: true,
prompt:
'Generate a Square invoice JSON object with location_id, order_id, primary_recipient, and payment_requests. Return ONLY the JSON object.',
generationType: 'json-object',
},
},
{
id: 'invoiceId',
title: 'Invoice ID',
type: 'short-input',
placeholder: 'Square invoice ID',
condition: {
field: 'operation',
value: ['get_invoice', 'publish_invoice', 'cancel_invoice', 'delete_invoice'],
},
required: {
field: 'operation',
value: ['get_invoice', 'publish_invoice', 'cancel_invoice', 'delete_invoice'],
},
},
{
id: 'version',
title: 'Invoice Version',
type: 'short-input',
placeholder: 'Current invoice version (e.g. 0)',
condition: {
field: 'operation',
value: ['publish_invoice', 'cancel_invoice', 'delete_invoice'],
},
required: { field: 'operation', value: ['publish_invoice', 'cancel_invoice'] },
},
// Catalog
{
id: 'object',
title: 'Catalog Object (JSON)',
type: 'code',
language: 'json',
placeholder: '{"type": "ITEM", "id": "#Coffee", "item_data": {"name": "Coffee"}}',
condition: { field: 'operation', value: 'upsert_catalog_object' },
required: { field: 'operation', value: 'upsert_catalog_object' },
wandConfig: {
enabled: true,
prompt:
'Generate a Square catalog object JSON for upsert. Use a temporary id like "#Name" for new objects. Return ONLY the JSON object.',
generationType: 'json-object',
},
},
{
id: 'objectId',
title: 'Catalog Object ID',
type: 'short-input',
placeholder: 'Square catalog object ID',
condition: {
field: 'operation',
value: ['get_catalog_object', 'create_catalog_image', 'delete_catalog_object'],
},
required: { field: 'operation', value: ['get_catalog_object', 'delete_catalog_object'] },
},
{
id: 'catalogObjectIds',
title: 'Catalog Object IDs (JSON Array)',
type: 'code',
language: 'json',
placeholder: '["variationId1", "variationId2"]',
condition: { field: 'operation', value: 'batch_retrieve_inventory_counts' },
mode: 'advanced',
},
{
id: 'states',
title: 'Inventory States (JSON Array)',
type: 'code',
language: 'json',
placeholder: '["IN_STOCK", "SOLD"]',
condition: { field: 'operation', value: 'batch_retrieve_inventory_counts' },
mode: 'advanced',
},
{
id: 'updatedAfter',
title: 'Updated After',
type: 'short-input',
placeholder: 'RFC 3339 timestamp',
condition: { field: 'operation', value: 'batch_retrieve_inventory_counts' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an RFC 3339 timestamp. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
},
{
id: 'includeRelatedObjects',
title: 'Include Related Objects',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: { field: 'operation', value: 'get_catalog_object' },
mode: 'advanced',
},
{
id: 'types',
title: 'Types',
type: 'short-input',
placeholder: 'Comma-separated (e.g. ITEM,CATEGORY)',
condition: { field: 'operation', value: 'list_catalog' },
mode: 'advanced',
},
{
id: 'objectTypes',
title: 'Object Types (JSON Array)',
type: 'code',
language: 'json',
placeholder: '["ITEM", "CATEGORY"]',
condition: { field: 'operation', value: 'search_catalog_objects' },
mode: 'advanced',
},
// Catalog image upload (file)
{
id: 'uploadFile',
title: 'Image File',
type: 'file-upload',
canonicalParamId: 'file',
placeholder: 'Upload an image',
acceptedTypes: 'image/*',
mode: 'basic',
multiple: false,
condition: { field: 'operation', value: 'create_catalog_image' },
required: { field: 'operation', value: 'create_catalog_image' },
},
{
id: 'fileRef',
title: 'Image File',
type: 'short-input',
canonicalParamId: 'file',
placeholder: 'Reference an image from previous blocks',
mode: 'advanced',
condition: { field: 'operation', value: 'create_catalog_image' },
required: { field: 'operation', value: 'create_catalog_image' },
},
{
id: 'fileName',
title: 'File Name',
type: 'short-input',
placeholder: 'Optional filename override',
condition: { field: 'operation', value: 'create_catalog_image' },
mode: 'advanced',
},
{
id: 'caption',
title: 'Caption',
type: 'short-input',
placeholder: 'Image caption (alt text)',
condition: { field: 'operation', value: 'create_catalog_image' },
},
// Shared pagination (list / search)
{
id: 'locationId',
title: 'Location ID',
type: 'short-input',
placeholder: 'Square location ID',
condition: {
field: 'operation',
value: [
'create_payment',
'list_payments',
'list_invoices',
'search_invoices',
'list_refunds',
'get_location',
],
},
required: { field: 'operation', value: ['list_invoices', 'search_invoices', 'get_location'] },
},
{
id: 'limit',
title: 'Limit',
type: 'short-input',
placeholder: 'Max results per page',
condition: {
field: 'operation',
value: [
'list_payments',
'list_refunds',
'list_customers',
'search_customers',
'search_orders',
'list_invoices',
'search_invoices',
'search_catalog_objects',
'batch_retrieve_inventory_counts',
],
},
mode: 'advanced',
},
{
id: 'cursor',
title: 'Cursor',
type: 'short-input',
placeholder: 'Pagination cursor from a previous response',
condition: {
field: 'operation',
value: [
'list_payments',
'list_refunds',
'list_customers',
'search_customers',
'search_orders',
'list_invoices',
'search_invoices',
'search_catalog_objects',
'list_catalog',
'batch_retrieve_inventory_counts',
],
},
mode: 'advanced',
},
// Idempotency (create-style operations)
{
id: 'idempotencyKey',
title: 'Idempotency Key',
type: 'short-input',
placeholder: 'Optional unique key (auto-generated if omitted)',
condition: {
field: 'operation',
value: [
'create_payment',
'refund_payment',
'create_customer',
'create_order',
'pay_order',
'create_invoice',
'publish_invoice',
'upsert_catalog_object',
'create_catalog_image',
],
},
mode: 'advanced',
},
],
tools: {
access: [
'square_create_payment',
'square_get_payment',
'square_list_payments',
'square_cancel_payment',
'square_complete_payment',
'square_refund_payment',
'square_get_refund',
'square_list_refunds',
'square_create_customer',
'square_get_customer',
'square_list_customers',
'square_search_customers',
'square_update_customer',
'square_delete_customer',
'square_list_locations',
'square_get_location',
'square_create_order',
'square_get_order',
'square_search_orders',
'square_pay_order',
'square_create_invoice',
'square_get_invoice',
'square_list_invoices',
'square_search_invoices',
'square_publish_invoice',
'square_cancel_invoice',
'square_delete_invoice',
'square_upsert_catalog_object',
'square_get_catalog_object',
'square_list_catalog',
'square_search_catalog_objects',
'square_create_catalog_image',
'square_delete_catalog_object',
'square_batch_retrieve_inventory_counts',
],
config: {
tool: (params) => `square_${params.operation}`,
params: (params) => {
const {
operation,
address,
query,
order,
invoice,
object,
locationIds,
objectTypes,
paymentIds,
catalogObjectIds,
states,
amount,
limit,
version,
orderVersion,
autocomplete,
includeRelatedObjects,
...rest
} = params
// The basic/advanced image inputs are collapsed into the canonical `file`
// param before this runs, so normalize from params.file.
const normalizedFile = normalizeFileInput(params.file, { single: true })
// Parse a JSON-typed input, naming the field in any error so the user
// knows exactly which input to fix, and validating the parsed shape so a
// valid-but-wrong-type value (e.g. a string where an array is expected)
// fails locally instead of as a confusing Square API error.
const parseJsonField = (
value: unknown,
field: string,
expected: 'object' | 'array'
): unknown => {
if (value === undefined || value === null || value === '') return undefined
let parsed: unknown = value
if (typeof value === 'string') {
try {
parsed = JSON.parse(value)
} catch (error) {
throw new Error(
`Invalid JSON in "${field}": ${error instanceof Error ? error.message : 'unknown error'}`
)
}
}
if (expected === 'array' && !Array.isArray(parsed)) {
throw new Error(`"${field}" must be a JSON array`)
}
if (
expected === 'object' &&
(parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
) {
throw new Error(`"${field}" must be a JSON object`)
}
return parsed
}
const parsedAddress = parseJsonField(address, 'address', 'object')
const parsedQuery = parseJsonField(query, 'query', 'object')
const parsedOrder = parseJsonField(order, 'order', 'object')
const parsedInvoice = parseJsonField(invoice, 'invoice', 'object')
const parsedObject = parseJsonField(object, 'object', 'object')
const parsedLocationIds = parseJsonField(locationIds, 'locationIds', 'array')
const parsedObjectTypes = parseJsonField(objectTypes, 'objectTypes', 'array')
const parsedPaymentIds = parseJsonField(paymentIds, 'paymentIds', 'array')
const parsedCatalogObjectIds = parseJsonField(catalogObjectIds, 'catalogObjectIds', 'array')
const parsedStates = parseJsonField(states, 'states', 'array')
// Coerce a numeric input, failing locally with a clear message rather than
// forwarding NaN to Square when the value is non-numeric.
const coerceNumber = (value: unknown, field: string): number | undefined => {
if (value === undefined || value === null || value === '') return undefined
const num = Number(value)
if (!Number.isFinite(num)) {
throw new Error(`"${field}" must be a valid number`)
}
return num
}
// Accept both the dropdown's string values and real booleans (which can
// arrive via connected blocks or templated inputs).
const coerceBoolean = (value: unknown): boolean => value === true || value === 'true'
const coercedAmount = coerceNumber(amount, 'amount')
const coercedLimit = coerceNumber(limit, 'limit')
const coercedVersion = coerceNumber(version, 'version')
const coercedOrderVersion = coerceNumber(orderVersion, 'orderVersion')
return {
...rest,
...(normalizedFile && { file: normalizedFile }),
...(parsedAddress !== undefined && { address: parsedAddress }),
...(parsedQuery !== undefined && { query: parsedQuery }),
...(parsedOrder !== undefined && { order: parsedOrder }),
...(parsedInvoice !== undefined && { invoice: parsedInvoice }),
...(parsedObject !== undefined && { object: parsedObject }),
...(parsedLocationIds !== undefined && { locationIds: parsedLocationIds }),
...(parsedObjectTypes !== undefined && { objectTypes: parsedObjectTypes }),
...(parsedPaymentIds !== undefined && { paymentIds: parsedPaymentIds }),
...(parsedCatalogObjectIds !== undefined && { catalogObjectIds: parsedCatalogObjectIds }),
...(parsedStates !== undefined && { states: parsedStates }),
...(coercedAmount !== undefined && { amount: coercedAmount }),
...(coercedLimit !== undefined && { limit: coercedLimit }),
...(coercedVersion !== undefined && { version: coercedVersion }),
...(coercedOrderVersion !== undefined && { orderVersion: coercedOrderVersion }),
...(autocomplete !== undefined && { autocomplete: coerceBoolean(autocomplete) }),
...(includeRelatedObjects !== undefined && {
includeRelatedObjects: coerceBoolean(includeRelatedObjects),
}),
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Square access token' },
sourceId: { type: 'string', description: 'Payment source ID' },
paymentId: { type: 'string', description: 'Payment ID' },
refundId: { type: 'string', description: 'Refund ID' },
status: { type: 'string', description: 'Status filter' },
amount: { type: 'number', description: 'Amount in smallest currency denomination' },
currency: { type: 'string', description: 'ISO 4217 currency code' },
reason: { type: 'string', description: 'Refund reason' },
versionToken: { type: 'string', description: 'Version token for payment concurrency control' },
autocomplete: { type: 'boolean', description: 'Capture payment immediately' },
beginTime: { type: 'string', description: 'Start of the reporting period (RFC 3339)' },
endTime: { type: 'string', description: 'End of the reporting period (RFC 3339)' },
customerId: { type: 'string', description: 'Customer ID' },
givenName: { type: 'string', description: 'Customer first name' },
familyName: { type: 'string', description: 'Customer last name' },
companyName: { type: 'string', description: 'Customer company name' },
nickname: { type: 'string', description: 'Customer nickname' },
emailAddress: { type: 'string', description: 'Customer email address' },
phoneNumber: { type: 'string', description: 'Customer phone number' },
birthday: { type: 'string', description: 'Customer birthday (YYYY-MM-DD or MM-DD)' },
address: { type: 'json', description: 'Customer address object' },
note: { type: 'string', description: 'Note' },
referenceId: { type: 'string', description: 'External reference ID' },
sortField: { type: 'string', description: 'Field to sort by' },
sortOrder: { type: 'string', description: 'Sort order (ASC or DESC)' },
query: { type: 'json', description: 'Search query object' },
order: { type: 'json', description: 'Order object' },
orderId: { type: 'string', description: 'Order ID' },
orderVersion: { type: 'number', description: 'Order version for payment' },
paymentIds: { type: 'json', description: 'Array of payment IDs to apply to an order' },
locationIds: { type: 'json', description: 'Array of location IDs' },
invoice: { type: 'json', description: 'Invoice object' },
invoiceId: { type: 'string', description: 'Invoice ID' },
version: { type: 'number', description: 'Invoice version' },
object: { type: 'json', description: 'Catalog object' },
objectId: { type: 'string', description: 'Catalog object ID' },
includeRelatedObjects: { type: 'boolean', description: 'Include related catalog objects' },
types: { type: 'string', description: 'Comma-separated catalog object types' },
objectTypes: { type: 'json', description: 'Array of catalog object types to search' },
catalogObjectIds: { type: 'json', description: 'Array of catalog object IDs for inventory' },
states: { type: 'json', description: 'Array of inventory states to filter by' },
updatedAfter: {
type: 'string',
description: 'Only return inventory counts updated after this time',
},
file: { type: 'json', description: 'Image file to upload (canonical param)' },
fileName: { type: 'string', description: 'Filename override for the image' },
caption: { type: 'string', description: 'Image caption' },
locationId: { type: 'string', description: 'Location ID' },
limit: { type: 'number', description: 'Maximum results to return' },
cursor: { type: 'string', description: 'Pagination cursor' },
idempotencyKey: { type: 'string', description: 'Idempotency key' },
},
outputs: {
payment: { type: 'json', description: 'Payment object' },
payments: { type: 'json', description: 'Array of payment objects' },
refund: { type: 'json', description: 'Refund object' },
refunds: { type: 'json', description: 'Array of refund objects' },
customer: { type: 'json', description: 'Customer object' },
customers: { type: 'json', description: 'Array of customer objects' },
location: { type: 'json', description: 'Location object' },
locations: { type: 'json', description: 'Array of location objects' },
order: { type: 'json', description: 'Order object' },
orders: { type: 'json', description: 'Array of order objects' },
invoice: { type: 'json', description: 'Invoice object' },
invoices: { type: 'json', description: 'Array of invoice objects' },
object: { type: 'json', description: 'Catalog object' },
objects: { type: 'json', description: 'Array of catalog objects' },
counts: { type: 'json', description: 'Array of inventory count objects' },
deleted: { type: 'boolean', description: 'Whether the resource was deleted' },
deleted_object_ids: { type: 'json', description: 'IDs of deleted catalog objects' },
deleted_at: { type: 'string', description: 'Timestamp when deletion occurred' },
id: { type: 'string', description: 'ID of the affected resource' },
metadata: { type: 'json', description: 'Operation summary metadata' },
},
}
export const SquareBlockMeta = {
tags: ['payments', 'subscriptions', 'automation'],
url: 'https://squareup.com',
templates: [
{
icon: SquareIcon,
title: 'Daily sales summary',
prompt:
'Build a scheduled daily workflow that lists Square payments from the previous day across all locations, totals gross sales, refunds, and net revenue, writes the figures to a table for historical tracking, and posts a Slack summary with day-over-day trends.',
modules: ['scheduled', 'tables', 'agent', 'workflows'],
category: 'operations',
tags: ['finance', 'reporting', 'founder'],
alsoIntegrations: ['slack'],
},
{
icon: SquareIcon,
title: 'Refund pattern monitor',
prompt:
'Create a scheduled weekly workflow that lists Square payments and their refunds, classifies each refund by reason and location, flags any location with an unusually high refund rate, and emails finance a narrative report with recommended actions.',
modules: ['scheduled', 'agent', 'files', 'workflows'],
category: 'operations',
tags: ['finance', 'analysis', 'monitoring'],
alsoIntegrations: ['gmail'],
},
{
icon: SquareIcon,
title: 'New customer welcome',
prompt:
'Build a workflow that takes a new Square customer, creates a welcome email tailored to their purchase, adds them to an onboarding tracking table, and posts a notification to the customer success Slack channel.',
modules: ['tables', 'agent', 'workflows'],
category: 'operations',
tags: ['sales', 'automation'],
alsoIntegrations: ['gmail', 'slack'],
},
{
icon: SquareIcon,
title: 'Invoice chase automation',
prompt:
'Create a scheduled workflow that lists Square invoices for a location, finds those that are unpaid past their due date, sends a polite reminder email per customer, and logs every chase action to a collections table.',
modules: ['scheduled', 'tables', 'agent', 'workflows'],
category: 'operations',
tags: ['finance', 'automation', 'reporting'],
alsoIntegrations: ['gmail'],
},
{
icon: SquareIcon,
title: 'Catalog image enrichment',
prompt:
'Build a workflow that lists Square catalog items missing images, generates a product image for each one, uploads it as a catalog image attached to the item, and writes a report of which items were updated.',
modules: ['agent', 'files', 'workflows'],
category: 'operations',
tags: ['ecommerce', 'automation'],
},
{
icon: SquareIcon,
title: 'Low-stock reorder alerts',
prompt:
'Create a scheduled workflow that lists the Square catalog, identifies items flagged as low or out of stock, drafts a reorder summary grouped by supplier, and posts it to a Slack purchasing channel with the items and quantities to reorder.',
modules: ['scheduled', 'agent', 'workflows'],
category: 'operations',
tags: ['ecommerce', 'operations'],
alsoIntegrations: ['slack'],
},
{
icon: SquareIcon,
title: 'Customer purchase history lookup',
prompt:
'Build a workflow that searches Square customers by email, pulls their orders and payments, summarizes lifetime spend and most-purchased items, and returns a concise profile the support team can use during a conversation.',
modules: ['agent', 'workflows'],
category: 'support',
tags: ['support', 'analysis'],
},
],
skills: [
{
name: 'take-payment',
description: 'Take a Square payment from a payment source and confirm the result.',
content:
'# Take Payment\n\nCharge a customer using Square.\n\n## Steps\n1. Run Create Payment with the source ID (card nonce or card on file), the amount in the smallest denomination, and the currency.\n2. Optionally attach a customer ID, location ID, or order ID.\n3. Confirm the result with Get Payment if you need the latest status.\n\n## Output\nReturn the payment ID, status (APPROVED, COMPLETED, or FAILED), and the amount charged. If a refund is needed, run Refund Payment with the payment ID.',
},
{
name: 'issue-and-publish-invoice',
description: 'Create a Square invoice for an order and publish it to the customer.',
content:
'# Issue And Publish Invoice\n\nBill a customer with a Square invoice.\n\n## Steps\n1. Make sure an order exists (use Create Order if needed) and you have the customer ID.\n2. Run Create Invoice with an invoice object referencing the location, order, primary recipient (customer), and payment requests. Note the returned invoice ID and version.\n3. Run Publish Invoice with that invoice ID and version to send it to the customer.\n4. Track payment with Get Invoice.\n\n## Output\nReturn the invoice ID, status, and the public URL where the customer can pay.',
},
{
name: 'manage-catalog-item',
description: 'Create or update a Square catalog item and attach an image to it.',
content:
'# Manage Catalog Item\n\nBuild out the Square catalog.\n\n## Steps\n1. Run Upsert Catalog Object with an ITEM object (use a temporary id like "#Name" for new items). Capture the returned object ID.\n2. To add a picture, run Create Catalog Image with the image file and the object ID to attach it to the item.\n3. Verify with Get Catalog Object or Search Catalog Objects.\n\n## Output\nReturn the catalog object ID, type, and version, plus the image object ID when an image was attached.',
},
{
name: 'find-customer-activity',
description: 'Look up a Square customer and summarize their orders and payments.',
content:
'# Find Customer Activity\n\nBuild a purchase history view for one customer.\n\n## Steps\n1. Run Search Customers (by email or phone) or Get Customer to identify the customer and their ID.\n2. Run Search Orders across the relevant locations filtered to that customer.\n3. Run List Payments and match payments to the customer for a full financial picture.\n\n## Output\nReturn the customer ID and email plus a summary of their orders and payments: total spend, number of orders, and any refunds.',
},
],
} as const satisfies BlockMeta
+3
View File
@@ -268,6 +268,7 @@ import { SlackBlock, SlackBlockMeta } from '@/blocks/blocks/slack'
import { SmtpBlock } from '@/blocks/blocks/smtp'
import { SpotifyBlock, SpotifyBlockMeta } from '@/blocks/blocks/spotify'
import { SQSBlock, SQSBlockMeta } from '@/blocks/blocks/sqs'
import { SquareBlock, SquareBlockMeta } from '@/blocks/blocks/square'
import { SSHBlock } from '@/blocks/blocks/ssh'
import { StagehandBlock, StagehandBlockMeta } from '@/blocks/blocks/stagehand'
import { StartTriggerBlock } from '@/blocks/blocks/start_trigger'
@@ -569,6 +570,7 @@ const BLOCK_REGISTRY: Record<string, BlockConfig> = {
smtp: SmtpBlock,
spotify: SpotifyBlock,
sqs: SQSBlock,
square: SquareBlock,
ssh: SSHBlock,
stagehand: StagehandBlock,
start_trigger: StartTriggerBlock,
@@ -827,6 +829,7 @@ const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
slack: SlackBlockMeta,
spotify: SpotifyBlockMeta,
sqs: SQSBlockMeta,
square: SquareBlockMeta,
stagehand: StagehandBlockMeta,
stripe: StripeBlockMeta,
sts: STSBlockMeta,
+11
View File
@@ -1958,6 +1958,17 @@ export function WhatsAppIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function SquareIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 1999.98 501.42' xmlns='http://www.w3.org/2000/svg'>
<path
fill='#fff'
d='M501.42,83.79v333.84c0,46.27-37.5,83.79-83.79,83.79H83.79c-46.28,0-83.79-37.5-83.79-83.79V83.79C0,37.51,37.52,0,83.79,0h333.84c46.29,0,83.79,37.5,83.79,83.79h0ZM410.22,117.64c0-14.61-11.85-26.45-26.45-26.45H117.62c-14.61,0-26.45,11.84-26.45,26.45v266.19c0,14.61,11.84,26.45,26.45,26.45h266.17c14.61,0,26.45-11.85,26.45-26.45V117.64h-.02ZM182.31,197.59c0-8.43,6.79-15.26,15.17-15.26h106.4c8.39,0,15.17,6.84,15.17,15.26v106.24c0,8.43-6.75,15.26-15.17,15.26h-106.4c-8.39,0-15.17-6.84-15.17-15.26v-106.24ZM778.93,221.94l-3.85-.86c-41.04-9.31-65.81-14.93-65.81-42,0-24.2,23.02-41.11,55.98-41.11,30.52,0,53.74,12.76,73.08,40.16,1.11,1.57,2.84,2.61,4.74,2.84,1.89.23,3.79-.35,5.23-1.59l32.16-27.71c2.68-2.31,3.15-6.22,1.1-9.09-24.19-33.89-67.01-54.12-114.56-54.12-31.56,0-60.34,9.26-81.04,26.08-21.73,17.65-33.21,41.93-33.21,70.23,0,63.76,54.74,76.94,98.71,87.53,4.45,1.08,8.77,2.1,12.95,3.08,39.74,9.36,66,15.54,66,43.74s-24.04,45.48-61.24,45.48c-33.71,0-64.35-17.1-86.28-48.14-1.1-1.55-2.8-2.59-4.68-2.84s-3.73.28-5.2,1.49l-33.86,27.99c-2.72,2.25-3.28,6.14-1.3,9.05,25.63,37.64,76.48,61.97,129.56,61.97,32.56,0,62.52-9.57,84.36-26.95,23.27-18.51,35.57-44.01,35.57-73.73,0-67.27-57.62-80.13-108.45-91.48l.04-.02ZM1126.33,177.73h-40.76c-3.74,0-6.78,3.04-6.78,6.78v19.06c-12.6-14.21-33.77-30.22-65.18-30.22s-56.88,12.32-75.37,35.62c-17.16,21.63-26.62,51.73-26.62,84.75s9.45,63.12,26.62,84.75c18.49,23.31,44.56,35.62,75.37,35.62,26.63,0,49.1-9.45,65.18-27.37v107.92c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78V184.51c0-3.74-3.04-6.78-6.78-6.78ZM1080.1,287.16v13.57c0,39.86-21.97,65.61-55.98,65.61-36.15,0-57.74-27.15-57.74-72.61s21.58-72.61,57.74-72.61c34.01,0,55.98,25.93,55.98,66.05h0ZM1360.59,177.73h-40.76c-3.74,0-6.78,3.04-6.78,6.78v130.66c0,32.45-23.32,49.42-46.36,49.42-26.03,0-39.79-15.58-39.79-45.04v-135.03c0-3.74-3.04-6.78-6.78-6.78h-40.76c-3.74,0-6.78,3.04-6.78,6.78v146.41c0,50.53,30.93,83.17,78.8,83.17,23.76,0,43.95-9.67,61.67-29.56v17.96c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78v-217.99c0-3.74-3.04-6.78-6.78-6.78h0ZM1607.92,367.06c-8.79-.24-12.72-4.78-12.72-14.7v-98.9c0-51.66-31.71-80.11-89.3-80.11-44.71,0-80.07,23.24-94.59,62.18-.66,1.78-.52,3.78.39,5.47.93,1.73,2.57,2.98,4.48,3.42l37.83,8.71c3.37.77,6.78-1.14,7.94-4.45,6.74-19.11,20.01-28.01,41.76-28.01,25.53,0,38.48,11.62,38.48,34.54v3.2l-62.73,12.98c-53.04,11.03-77.74,34.25-77.74,73.09s32.22,68.73,78.36,68.73c28.25,0,51.2-8.69,66.46-25.16,9.13,17.73,33.93,26.29,62.3,21.35,3.24-.57,5.6-3.38,5.6-6.69v-28.9c0-3.63-2.93-6.67-6.52-6.77v.02ZM1542.2,300.53v26.89c0,27.56-27.28,41.98-54.24,41.98-20.26,0-32.35-10.13-32.35-27.1,0-19.37,14.63-26.41,38.23-31.5l48.36-10.27ZM1774.34,177.32c-3.17-.52-6.47-.77-10.09-.77-27.5,0-50.92,12.35-62.11,32.47v-24.51c0-3.74-3.04-6.78-6.78-6.78h-40.76c-3.74,0-6.78,3.04-6.78,6.78v217.99c0,3.74,3.04,6.78,6.78,6.78h40.76c3.74,0,6.78-3.04,6.78-6.78v-114.9c0-34.27,23.2-57.3,57.74-57.3,4.7,0,8.63.17,12.74.56,1.89.18,3.79-.45,5.2-1.73s2.22-3.11,2.22-5.02v-40.09c0-3.34-2.39-6.16-5.69-6.7h-.01ZM1973.74,206.72c-18.32-21.83-44.82-33.36-76.62-33.36s-59.09,12.34-79.33,34.76c-19.98,22.12-30.98,52.52-30.98,85.61,0,70.87,46.26,120.37,112.49,120.37,43.9,0,79.17-21.53,96.77-59.08.8-1.7.85-3.61.14-5.37-.71-1.76-2.14-3.15-3.91-3.82l-33.69-12.76c-3.39-1.28-7.27.37-8.63,3.68-8.08,19.63-26.56,30.9-50.68,30.9-33.08,0-56.1-23.59-60.26-61.64h154.16c3.74,0,6.78-3.04,6.78-6.78v-11.63c0-31.99-9.32-60.72-26.25-80.88h.01ZM1945.21,264.82h-103.37c7.73-28.91,27.66-45.45,54.84-45.45,34.72,0,47.8,23.3,48.52,45.45h.01Z'
/>
</svg>
)
}
export function StripeIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
@@ -28,6 +28,7 @@ export * from './sap'
export * from './search'
export * from './servicenow'
export * from './shared'
export * from './square'
export * from './stagehand'
export * from './thinking'
export * from './typeform'
@@ -0,0 +1,44 @@
import { z } from 'zod'
import { defineRouteContract } from '@/lib/api/contracts/types'
import { FileInputSchema } from '@/lib/uploads/utils/file-schemas'
/**
* Internal contract for the Square catalog image upload route. The route
* downloads the referenced file from storage and forwards it to Square's
* multipart `CreateCatalogImage` endpoint, returning the created image object.
*
* The `output.object` is a Square CatalogObject, whose shape is polymorphic by
* `type`, so it is intentionally left opaque while the envelope is typed.
*/
const squareCatalogImageResponseSchema = z.object({
success: z.boolean(),
output: z
.object({
object: z.unknown(),
metadata: z.object({
id: z.string(),
type: z.string().nullable(),
version: z.number().nullable(),
}),
})
.optional(),
error: z.string().optional(),
})
export const squareCatalogImageBodySchema = z.object({
accessToken: z.string().min(1, 'Access token is required'),
file: FileInputSchema.optional().nullable(),
fileName: z.string().optional().nullable(),
objectId: z.string().optional().nullable(),
caption: z.string().optional().nullable(),
idempotencyKey: z.string().optional().nullable(),
})
export type SquareCatalogImageBody = z.input<typeof squareCatalogImageBodySchema>
export const squareCatalogImageContract = defineRouteContract({
method: 'POST',
path: '/api/tools/square/catalog-image',
body: squareCatalogImageBodySchema,
response: { mode: 'json', schema: squareCatalogImageResponseSchema },
})
@@ -190,6 +190,7 @@ import {
SlackIcon,
SmtpIcon,
SQSIcon,
SquareIcon,
SshIcon,
STSIcon,
STTIcon,
@@ -415,6 +416,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
slack: SlackIcon,
smtp: SmtpIcon,
sqs: SQSIcon,
square: SquareIcon,
ssh: SshIcon,
stagehand: StagehandIcon,
stripe: StripeIcon,
+155
View File
@@ -14660,6 +14660,161 @@
"category": "tools",
"integrationType": "email"
},
{
"type": "square",
"slug": "square",
"name": "Square",
"description": "Process payments and manage Square commerce data",
"longDescription": "Integrate Square into the workflow. Take and refund payments, manage customers, build catalog items and images, create and search orders, and issue invoices. Authenticate with a Square access token (personal access token).",
"bgColor": "#000000",
"iconName": "SquareIcon",
"docsUrl": "https://docs.sim.ai/integrations/square",
"operations": [
{
"name": "Create Payment",
"description": "Take a payment using a payment source such as a card nonce or a card on file"
},
{
"name": "Get Payment",
"description": "Retrieve details for a single payment by its ID"
},
{
"name": "List Payments",
"description": "List payments taken by the account, optionally filtered by location and time range"
},
{
"name": "Cancel Payment",
"description": "Cancel (void) an authorized payment that has not been captured"
},
{
"name": "Complete Payment",
"description": "Capture (complete) a payment that was authorized with delayed capture"
},
{
"name": "Refund Payment",
"description": "Refund all or part of a completed payment"
},
{
"name": "Get Refund",
"description": "Retrieve a single payment refund by its ID"
},
{
"name": "List Refunds",
"description": "List payment refunds, optionally filtered by location, status, and time range"
},
{
"name": "Create Customer",
"description": "Create a new customer profile in the Square customer directory"
},
{
"name": "Get Customer",
"description": "Retrieve a single customer profile by its ID"
},
{
"name": "List Customers",
"description": "List customer profiles in the Square customer directory"
},
{
"name": "Search Customers",
"description": "Search customer profiles using filters such as email, phone, or creation date"
},
{
"name": "Update Customer",
"description": "Update fields on an existing customer profile"
},
{
"name": "Delete Customer",
"description": "Delete a customer profile from the Square customer directory"
},
{
"name": "List Locations",
"description": "List all locations associated with the Square account"
},
{
"name": "Get Location",
"description": "Retrieve a single location by its ID"
},
{
"name": "Create Order",
"description": "Create an order with line items, taxes, discounts, and fulfillments"
},
{
"name": "Get Order",
"description": "Retrieve a single order by its ID"
},
{
"name": "Search Orders",
"description": "Search orders across one or more locations using filters and sorting"
},
{
"name": "Pay Order",
"description": "Pay for an order using one or more already-approved payments"
},
{
"name": "Create Invoice",
"description": "Create a draft invoice for an existing order and customer"
},
{
"name": "Get Invoice",
"description": "Retrieve a single invoice by its ID"
},
{
"name": "List Invoices",
"description": "List invoices for a specific location"
},
{
"name": "Search Invoices",
"description": "Search invoices across one or more locations"
},
{
"name": "Publish Invoice",
"description": "Publish a draft invoice so it is sent to the customer and becomes payable"
},
{
"name": "Cancel Invoice",
"description": "Cancel a published invoice that is unpaid or partially paid"
},
{
"name": "Delete Invoice",
"description": "Delete a draft invoice"
},
{
"name": "Upsert Catalog Object",
"description": "Create or update a catalog object such as an item, variation, or category"
},
{
"name": "Get Catalog Object",
"description": "Retrieve a single catalog object by its ID"
},
{
"name": "List Catalog",
"description": "List catalog objects, optionally filtered by type"
},
{
"name": "Search Catalog Objects",
"description": "Search catalog objects by type and query filters"
},
{
"name": "Create Catalog Image",
"description": "Upload an image and attach it to the catalog, optionally to a specific item"
},
{
"name": "Delete Catalog Object",
"description": "Delete a catalog object and its children (e.g. an item and its variations)"
},
{
"name": "Batch Retrieve Inventory Counts",
"description": "Retrieve current inventory counts for catalog items across locations"
}
],
"operationCount": 34,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
"category": "tools",
"integrationType": "commerce",
"tags": ["payments", "subscriptions", "automation"]
},
{
"type": "ssh",
"slug": "ssh",
+12
View File
@@ -176,6 +176,17 @@ const ERROR_EXTRACTORS: ErrorExtractorConfig[] = [
examples: ['Hunter.io API'],
extract: (errorInfo) => errorInfo?.data?.errors?.[0]?.details,
},
{
id: 'square-errors',
description: 'Square API error format with errors[].detail and errors[].code',
examples: ['Square API'],
extract: (errorInfo) => {
const err = errorInfo?.data?.errors?.[0]
if (!err) return undefined
if (err.detail) return err.code ? `${err.detail} (${err.code})` : err.detail
return err.code
},
},
{
id: 'errors-array-string',
description: 'Errors array containing strings or objects with messages',
@@ -302,6 +313,7 @@ export const ErrorExtractorId = {
BATCH_VALIDATION_ERRORS: 'batch-validation-errors',
NESTJS_VALIDATION_ERRORS: 'nestjs-validation-errors',
HUNTER_ERRORS: 'hunter-errors',
SQUARE_ERRORS: 'square-errors',
ERRORS_ARRAY_STRING: 'errors-array-string',
TELEGRAM_DESCRIPTION: 'telegram-description',
STANDARD_MESSAGE: 'standard-message',
+70
View File
@@ -3135,6 +3135,42 @@ import {
spotifyUpdatePlaylistTool,
} from '@/tools/spotify'
import { sqsSendTool } from '@/tools/sqs'
import {
squareBatchRetrieveInventoryCountsTool,
squareCancelInvoiceTool,
squareCancelPaymentTool,
squareCompletePaymentTool,
squareCreateCatalogImageTool,
squareCreateCustomerTool,
squareCreateInvoiceTool,
squareCreateOrderTool,
squareCreatePaymentTool,
squareDeleteCatalogObjectTool,
squareDeleteCustomerTool,
squareDeleteInvoiceTool,
squareGetCatalogObjectTool,
squareGetCustomerTool,
squareGetInvoiceTool,
squareGetLocationTool,
squareGetOrderTool,
squareGetPaymentTool,
squareGetRefundTool,
squareListCatalogTool,
squareListCustomersTool,
squareListInvoicesTool,
squareListLocationsTool,
squareListPaymentsTool,
squareListRefundsTool,
squarePayOrderTool,
squarePublishInvoiceTool,
squareRefundPaymentTool,
squareSearchCatalogObjectsTool,
squareSearchCustomersTool,
squareSearchInvoicesTool,
squareSearchOrdersTool,
squareUpdateCustomerTool,
squareUpsertCatalogObjectTool,
} from '@/tools/square'
import {
sshCheckCommandExistsTool,
sshCheckFileExistsTool,
@@ -7045,6 +7081,40 @@ export const tools: Record<string, ToolConfig> = {
spotify_set_repeat: spotifySetRepeatTool,
spotify_set_shuffle: spotifySetShuffleTool,
spotify_transfer_playback: spotifyTransferPlaybackTool,
square_create_payment: squareCreatePaymentTool,
square_get_payment: squareGetPaymentTool,
square_list_payments: squareListPaymentsTool,
square_cancel_payment: squareCancelPaymentTool,
square_complete_payment: squareCompletePaymentTool,
square_refund_payment: squareRefundPaymentTool,
square_get_refund: squareGetRefundTool,
square_list_refunds: squareListRefundsTool,
square_create_customer: squareCreateCustomerTool,
square_get_customer: squareGetCustomerTool,
square_list_customers: squareListCustomersTool,
square_search_customers: squareSearchCustomersTool,
square_update_customer: squareUpdateCustomerTool,
square_delete_customer: squareDeleteCustomerTool,
square_list_locations: squareListLocationsTool,
square_get_location: squareGetLocationTool,
square_create_order: squareCreateOrderTool,
square_get_order: squareGetOrderTool,
square_search_orders: squareSearchOrdersTool,
square_pay_order: squarePayOrderTool,
square_create_invoice: squareCreateInvoiceTool,
square_get_invoice: squareGetInvoiceTool,
square_list_invoices: squareListInvoicesTool,
square_search_invoices: squareSearchInvoicesTool,
square_publish_invoice: squarePublishInvoiceTool,
square_cancel_invoice: squareCancelInvoiceTool,
square_delete_invoice: squareDeleteInvoiceTool,
square_upsert_catalog_object: squareUpsertCatalogObjectTool,
square_get_catalog_object: squareGetCatalogObjectTool,
square_list_catalog: squareListCatalogTool,
square_search_catalog_objects: squareSearchCatalogObjectsTool,
square_create_catalog_image: squareCreateCatalogImageTool,
square_delete_catalog_object: squareDeleteCatalogObjectTool,
square_batch_retrieve_inventory_counts: squareBatchRetrieveInventoryCountsTool,
upstash_redis_get: upstashRedisGetTool,
upstash_redis_set: upstashRedisSetTool,
upstash_redis_delete: upstashRedisDeleteTool,
@@ -0,0 +1,112 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type {
BatchRetrieveInventoryCountsParams,
InventoryCountListResponse,
} from '@/tools/square/types'
import {
INVENTORY_COUNT_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareBatchRetrieveInventoryCountsTool: ToolConfig<
BatchRetrieveInventoryCountsParams,
InventoryCountListResponse
> = {
id: 'square_batch_retrieve_inventory_counts',
name: 'Square Batch Retrieve Inventory Counts',
description: 'Retrieve current inventory counts for catalog items across locations',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
catalogObjectIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'IDs of the catalog item variations to retrieve counts for',
},
locationIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'IDs of the locations to retrieve counts for (defaults to all locations)',
},
states: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Inventory states to filter by (e.g. IN_STOCK, SOLD, IN_TRANSIT)',
},
updatedAfter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only return counts updated after this RFC 3339 timestamp',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page (1-1000)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/inventory/counts/batch-retrieve`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.catalogObjectIds) body.catalog_object_ids = params.catalogObjectIds
if (params.locationIds) body.location_ids = params.locationIds
if (params.states) body.states = params.states
if (params.updatedAfter) body.updated_after = params.updatedAfter
if (params.limit !== undefined) body.limit = params.limit
if (params.cursor) body.cursor = params.cursor
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const counts = data.counts ?? []
return {
success: true,
output: {
counts,
metadata: {
count: counts.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
counts: {
type: 'array',
description: 'Array of inventory count objects',
items: INVENTORY_COUNT_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+71
View File
@@ -0,0 +1,71 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CancelInvoiceParams, InvoiceResponse } from '@/tools/square/types'
import {
INVOICE_METADATA_OUTPUT_PROPERTIES,
INVOICE_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCancelInvoiceTool: ToolConfig<CancelInvoiceParams, InvoiceResponse> = {
id: 'square_cancel_invoice',
name: 'Square Cancel Invoice',
description: 'Cancel a published invoice that is unpaid or partially paid',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
invoiceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the invoice to cancel',
},
version: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Current version of the invoice',
},
},
request: {
url: (params) =>
`${SQUARE_BASE_URL}/v2/invoices/${encodeURIComponent(params.invoiceId)}/cancel`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => ({ version: params.version }),
},
transformResponse: async (response) => {
const data = await response.json()
const invoice = data.invoice ?? {}
return {
success: true,
output: {
invoice,
metadata: {
id: invoice.id,
status: invoice.status ?? null,
version: invoice.version ?? null,
},
},
}
},
outputs: {
invoice: { ...INVOICE_OUTPUT, description: 'The canceled invoice object' },
metadata: {
type: 'json',
description: 'Invoice summary metadata',
properties: INVOICE_METADATA_OUTPUT_PROPERTIES,
},
},
}
+65
View File
@@ -0,0 +1,65 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CancelPaymentParams, PaymentResponse } from '@/tools/square/types'
import {
PAYMENT_METADATA_OUTPUT_PROPERTIES,
PAYMENT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCancelPaymentTool: ToolConfig<CancelPaymentParams, PaymentResponse> = {
id: 'square_cancel_payment',
name: 'Square Cancel Payment',
description: 'Cancel (void) an authorized payment that has not been captured',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
paymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the payment to cancel',
},
},
request: {
url: (params) =>
`${SQUARE_BASE_URL}/v2/payments/${encodeURIComponent(params.paymentId)}/cancel`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: () => ({}),
},
transformResponse: async (response) => {
const data = await response.json()
const payment = data.payment ?? {}
return {
success: true,
output: {
payment,
metadata: {
id: payment.id,
status: payment.status ?? null,
order_id: payment.order_id ?? null,
},
},
}
},
outputs: {
payment: { ...PAYMENT_OUTPUT, description: 'The canceled payment object' },
metadata: {
type: 'json',
description: 'Payment summary metadata',
properties: PAYMENT_METADATA_OUTPUT_PROPERTIES,
},
},
}
+71
View File
@@ -0,0 +1,71 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CompletePaymentParams, PaymentResponse } from '@/tools/square/types'
import {
PAYMENT_METADATA_OUTPUT_PROPERTIES,
PAYMENT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCompletePaymentTool: ToolConfig<CompletePaymentParams, PaymentResponse> = {
id: 'square_complete_payment',
name: 'Square Complete Payment',
description: 'Capture (complete) a payment that was authorized with delayed capture',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
paymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the payment to complete',
},
versionToken: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional version token for optimistic concurrency control',
},
},
request: {
url: (params) =>
`${SQUARE_BASE_URL}/v2/payments/${encodeURIComponent(params.paymentId)}/complete`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => (params.versionToken ? { version_token: params.versionToken } : {}),
},
transformResponse: async (response) => {
const data = await response.json()
const payment = data.payment ?? {}
return {
success: true,
output: {
payment,
metadata: {
id: payment.id,
status: payment.status ?? null,
order_id: payment.order_id ?? null,
},
},
}
},
outputs: {
payment: { ...PAYMENT_OUTPUT, description: 'The completed payment object' },
metadata: {
type: 'json',
description: 'Payment summary metadata',
properties: PAYMENT_METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,91 @@
import type { CatalogObjectResponse, CreateCatalogImageParams } from '@/tools/square/types'
import {
CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
CATALOG_OBJECT_OUTPUT,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCreateCatalogImageTool: ToolConfig<
CreateCatalogImageParams,
CatalogObjectResponse
> = {
id: 'square_create_catalog_image',
name: 'Square Create Catalog Image',
description: 'Upload an image and attach it to the catalog, optionally to a specific item',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
file: {
type: 'file',
required: true,
visibility: 'user-or-llm',
description: 'The image file to upload (UserFile object)',
},
fileName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional filename override for the image',
},
objectId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the catalog object (e.g. an item) to attach the image to',
},
caption: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Caption (alt text) for the image',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: '/api/tools/square/catalog-image',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => ({
accessToken: params.apiKey,
file: params.file,
fileName: params.fileName,
objectId: params.objectId,
caption: params.caption,
idempotencyKey: params.idempotencyKey,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
throw new Error(data.error || 'Failed to upload catalog image')
}
return {
success: true,
output: data.output,
}
},
outputs: {
object: { ...CATALOG_OBJECT_OUTPUT, description: 'The created catalog image object' },
metadata: {
type: 'json',
description: 'Catalog object summary metadata',
properties: CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
},
},
}
+141
View File
@@ -0,0 +1,141 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CreateCustomerParams, CustomerResponse } from '@/tools/square/types'
import {
CUSTOMER_METADATA_OUTPUT_PROPERTIES,
CUSTOMER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCreateCustomerTool: ToolConfig<CreateCustomerParams, CustomerResponse> = {
id: 'square_create_customer',
name: 'Square Create Customer',
description: 'Create a new customer profile in the Square customer directory',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
givenName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'First name of the customer',
},
familyName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Last name of the customer',
},
companyName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Business name of the customer',
},
nickname: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Nickname of the customer',
},
emailAddress: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Email address of the customer',
},
phoneNumber: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Phone number of the customer',
},
birthday: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Birthday in YYYY-MM-DD or MM-DD format',
},
note: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Note about the customer',
},
referenceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional external reference for the customer',
},
address: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Square address object for the customer',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/customers`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
idempotency_key: params.idempotencyKey || generateId(),
}
if (params.givenName) body.given_name = params.givenName
if (params.familyName) body.family_name = params.familyName
if (params.companyName) body.company_name = params.companyName
if (params.nickname) body.nickname = params.nickname
if (params.emailAddress) body.email_address = params.emailAddress
if (params.phoneNumber) body.phone_number = params.phoneNumber
if (params.birthday) body.birthday = params.birthday
if (params.note) body.note = params.note
if (params.referenceId) body.reference_id = params.referenceId
if (params.address) body.address = params.address
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const customer = data.customer ?? {}
return {
success: true,
output: {
customer,
metadata: {
id: customer.id,
email_address: customer.email_address ?? null,
given_name: customer.given_name ?? null,
family_name: customer.family_name ?? null,
},
},
}
},
outputs: {
customer: { ...CUSTOMER_OUTPUT, description: 'The created customer object' },
metadata: {
type: 'json',
description: 'Customer summary metadata',
properties: CUSTOMER_METADATA_OUTPUT_PROPERTIES,
},
},
}
+75
View File
@@ -0,0 +1,75 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CreateInvoiceParams, InvoiceResponse } from '@/tools/square/types'
import {
INVOICE_METADATA_OUTPUT_PROPERTIES,
INVOICE_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCreateInvoiceTool: ToolConfig<CreateInvoiceParams, InvoiceResponse> = {
id: 'square_create_invoice',
name: 'Square Create Invoice',
description: 'Create a draft invoice for an existing order and customer',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
invoice: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Square invoice object including location_id, order_id, primary_recipient, and payment_requests (e.g. {"location_id":"L1","order_id":"O1","primary_recipient":{"customer_id":"C1"},"payment_requests":[{"request_type":"BALANCE","due_date":"2026-07-01"}]})',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/invoices`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => ({
idempotency_key: params.idempotencyKey || generateId(),
invoice: params.invoice,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const invoice = data.invoice ?? {}
return {
success: true,
output: {
invoice,
metadata: {
id: invoice.id,
status: invoice.status ?? null,
version: invoice.version ?? null,
},
},
}
},
outputs: {
invoice: { ...INVOICE_OUTPUT, description: 'The created invoice object' },
metadata: {
type: 'json',
description: 'Invoice summary metadata',
properties: INVOICE_METADATA_OUTPUT_PROPERTIES,
},
},
}
+75
View File
@@ -0,0 +1,75 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CreateOrderParams, OrderResponse } from '@/tools/square/types'
import {
ORDER_METADATA_OUTPUT_PROPERTIES,
ORDER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCreateOrderTool: ToolConfig<CreateOrderParams, OrderResponse> = {
id: 'square_create_order',
name: 'Square Create Order',
description: 'Create an order with line items, taxes, discounts, and fulfillments',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
order: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Square order object including location_id and line_items (e.g. {"location_id":"L1","line_items":[{"name":"Coffee","quantity":"1","base_price_money":{"amount":250,"currency":"USD"}}]})',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/orders`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => ({
idempotency_key: params.idempotencyKey || generateId(),
order: params.order,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const order = data.order ?? {}
return {
success: true,
output: {
order,
metadata: {
id: order.id,
state: order.state ?? null,
location_id: order.location_id ?? null,
},
},
}
},
outputs: {
order: { ...ORDER_OUTPUT, description: 'The created order object' },
metadata: {
type: 'json',
description: 'Order summary metadata',
properties: ORDER_METADATA_OUTPUT_PROPERTIES,
},
},
}
+132
View File
@@ -0,0 +1,132 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CreatePaymentParams, PaymentResponse } from '@/tools/square/types'
import {
PAYMENT_METADATA_OUTPUT_PROPERTIES,
PAYMENT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareCreatePaymentTool: ToolConfig<CreatePaymentParams, PaymentResponse> = {
id: 'square_create_payment',
name: 'Square Create Payment',
description: 'Take a payment using a payment source such as a card nonce or a card on file',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
sourceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the payment source (card nonce, card-on-file ID, or wallet token)',
},
amount: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Amount in the smallest currency denomination (e.g. 1000 = $10.00)',
},
currency: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Three-letter ISO 4217 currency code (e.g. USD)',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
customerId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the customer associated with the payment',
},
locationId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the location where the payment is taken (defaults to the main location)',
},
orderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the order associated with the payment',
},
referenceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional external reference for the payment',
},
note: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional note attached to the payment',
},
autocomplete: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to immediately capture the payment (defaults to true)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/payments`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
idempotency_key: params.idempotencyKey || generateId(),
source_id: params.sourceId,
amount_money: { amount: params.amount, currency: params.currency },
}
if (params.customerId) body.customer_id = params.customerId
if (params.locationId) body.location_id = params.locationId
if (params.orderId) body.order_id = params.orderId
if (params.referenceId) body.reference_id = params.referenceId
if (params.note) body.note = params.note
if (params.autocomplete !== undefined) body.autocomplete = params.autocomplete
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const payment = data.payment ?? {}
return {
success: true,
output: {
payment,
metadata: {
id: payment.id,
status: payment.status ?? null,
order_id: payment.order_id ?? null,
},
},
}
},
outputs: {
payment: { ...PAYMENT_OUTPUT, description: 'The created payment object' },
metadata: {
type: 'json',
description: 'Payment summary metadata',
properties: PAYMENT_METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,62 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CatalogDeleteResponse, DeleteCatalogObjectParams } from '@/tools/square/types'
import { SQUARE_BASE_URL, squareHeaders } from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareDeleteCatalogObjectTool: ToolConfig<
DeleteCatalogObjectParams,
CatalogDeleteResponse
> = {
id: 'square_delete_catalog_object',
name: 'Square Delete Catalog Object',
description: 'Delete a catalog object and its children (e.g. an item and its variations)',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
objectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the catalog object to delete',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/catalog/object/${encodeURIComponent(params.objectId)}`,
method: 'DELETE',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
deleted: true,
deleted_object_ids: data.deleted_object_ids ?? [],
deleted_at: data.deleted_at ?? null,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the catalog object was deleted' },
deleted_object_ids: {
type: 'array',
description: 'IDs of all catalog objects deleted (including children)',
items: { type: 'string' },
},
deleted_at: {
type: 'string',
description: 'Timestamp when the deletion occurred (RFC 3339)',
optional: true,
},
},
}
+49
View File
@@ -0,0 +1,49 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CustomerDeleteResponse, DeleteCustomerParams } from '@/tools/square/types'
import { SQUARE_BASE_URL, squareHeaders } from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareDeleteCustomerTool: ToolConfig<DeleteCustomerParams, CustomerDeleteResponse> = {
id: 'square_delete_customer',
name: 'Square Delete Customer',
description: 'Delete a customer profile from the Square customer directory',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
customerId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the customer to delete',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/customers/${encodeURIComponent(params.customerId)}`,
method: 'DELETE',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response, params) => {
await response.json().catch(() => ({}))
return {
success: true,
output: {
deleted: true,
id: params?.customerId ?? '',
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the customer was deleted' },
id: { type: 'string', description: 'ID of the deleted customer' },
},
}
+60
View File
@@ -0,0 +1,60 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { DeleteInvoiceParams, InvoiceDeleteResponse } from '@/tools/square/types'
import { SQUARE_BASE_URL, squareHeaders } from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareDeleteInvoiceTool: ToolConfig<DeleteInvoiceParams, InvoiceDeleteResponse> = {
id: 'square_delete_invoice',
name: 'Square Delete Invoice',
description: 'Delete a draft invoice',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
invoiceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the draft invoice to delete',
},
version: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Current version of the invoice (required if the invoice has been updated)',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/invoices/${encodeURIComponent(params.invoiceId)}`)
if (params.version !== undefined)
url.searchParams.append('version', params.version.toString())
return url.toString()
},
method: 'DELETE',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response, params) => {
await response.json().catch(() => ({}))
return {
success: true,
output: {
deleted: true,
id: params?.invoiceId ?? '',
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the invoice was deleted' },
id: { type: 'string', description: 'ID of the deleted invoice' },
},
}
@@ -0,0 +1,78 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CatalogObjectResponse, GetCatalogObjectParams } from '@/tools/square/types'
import {
CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
CATALOG_OBJECT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetCatalogObjectTool: ToolConfig<GetCatalogObjectParams, CatalogObjectResponse> =
{
id: 'square_get_catalog_object',
name: 'Square Get Catalog Object',
description: 'Retrieve a single catalog object by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
objectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the catalog object to retrieve',
},
includeRelatedObjects: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to include related objects such as an item variations',
},
},
request: {
url: (params) => {
const url = new URL(
`${SQUARE_BASE_URL}/v2/catalog/object/${encodeURIComponent(params.objectId)}`
)
if (params.includeRelatedObjects !== undefined) {
url.searchParams.append('include_related_objects', String(params.includeRelatedObjects))
}
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const object = data.object ?? {}
return {
success: true,
output: {
object,
metadata: {
id: object.id,
type: object.type ?? null,
version: object.version ?? null,
},
},
}
},
outputs: {
object: { ...CATALOG_OBJECT_OUTPUT, description: 'The retrieved catalog object' },
metadata: {
type: 'json',
description: 'Catalog object summary metadata',
properties: CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
},
},
}
+64
View File
@@ -0,0 +1,64 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CustomerResponse, GetCustomerParams } from '@/tools/square/types'
import {
CUSTOMER_METADATA_OUTPUT_PROPERTIES,
CUSTOMER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetCustomerTool: ToolConfig<GetCustomerParams, CustomerResponse> = {
id: 'square_get_customer',
name: 'Square Get Customer',
description: 'Retrieve a single customer profile by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
customerId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the customer to retrieve',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/customers/${encodeURIComponent(params.customerId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const customer = data.customer ?? {}
return {
success: true,
output: {
customer,
metadata: {
id: customer.id,
email_address: customer.email_address ?? null,
given_name: customer.given_name ?? null,
family_name: customer.family_name ?? null,
},
},
}
},
outputs: {
customer: { ...CUSTOMER_OUTPUT, description: 'The retrieved customer object' },
metadata: {
type: 'json',
description: 'Customer summary metadata',
properties: CUSTOMER_METADATA_OUTPUT_PROPERTIES,
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { GetInvoiceParams, InvoiceResponse } from '@/tools/square/types'
import {
INVOICE_METADATA_OUTPUT_PROPERTIES,
INVOICE_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetInvoiceTool: ToolConfig<GetInvoiceParams, InvoiceResponse> = {
id: 'square_get_invoice',
name: 'Square Get Invoice',
description: 'Retrieve a single invoice by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
invoiceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the invoice to retrieve',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/invoices/${encodeURIComponent(params.invoiceId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const invoice = data.invoice ?? {}
return {
success: true,
output: {
invoice,
metadata: {
id: invoice.id,
status: invoice.status ?? null,
version: invoice.version ?? null,
},
},
}
},
outputs: {
invoice: { ...INVOICE_OUTPUT, description: 'The retrieved invoice object' },
metadata: {
type: 'json',
description: 'Invoice summary metadata',
properties: INVOICE_METADATA_OUTPUT_PROPERTIES,
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { GetLocationParams, LocationResponse } from '@/tools/square/types'
import { LOCATION_OUTPUT, SQUARE_BASE_URL, squareHeaders } from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetLocationTool: ToolConfig<GetLocationParams, LocationResponse> = {
id: 'square_get_location',
name: 'Square Get Location',
description: 'Retrieve a single location by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the location to retrieve (use "main" for the main location)',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/locations/${encodeURIComponent(params.locationId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const location = data.location ?? {}
return {
success: true,
output: {
location,
metadata: {
id: location.id,
name: location.name ?? null,
},
},
}
},
outputs: {
location: { ...LOCATION_OUTPUT, description: 'The retrieved location object' },
metadata: {
type: 'json',
description: 'Location summary metadata',
properties: {
id: { type: 'string', description: 'Square location ID' },
name: { type: 'string', description: 'Location name', optional: true },
},
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { GetOrderParams, OrderResponse } from '@/tools/square/types'
import {
ORDER_METADATA_OUTPUT_PROPERTIES,
ORDER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetOrderTool: ToolConfig<GetOrderParams, OrderResponse> = {
id: 'square_get_order',
name: 'Square Get Order',
description: 'Retrieve a single order by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the order to retrieve',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/orders/${encodeURIComponent(params.orderId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const order = data.order ?? {}
return {
success: true,
output: {
order,
metadata: {
id: order.id,
state: order.state ?? null,
location_id: order.location_id ?? null,
},
},
}
},
outputs: {
order: { ...ORDER_OUTPUT, description: 'The retrieved order object' },
metadata: {
type: 'json',
description: 'Order summary metadata',
properties: ORDER_METADATA_OUTPUT_PROPERTIES,
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { GetPaymentParams, PaymentResponse } from '@/tools/square/types'
import {
PAYMENT_METADATA_OUTPUT_PROPERTIES,
PAYMENT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetPaymentTool: ToolConfig<GetPaymentParams, PaymentResponse> = {
id: 'square_get_payment',
name: 'Square Get Payment',
description: 'Retrieve details for a single payment by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
paymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the payment to retrieve',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/payments/${encodeURIComponent(params.paymentId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const payment = data.payment ?? {}
return {
success: true,
output: {
payment,
metadata: {
id: payment.id,
status: payment.status ?? null,
order_id: payment.order_id ?? null,
},
},
}
},
outputs: {
payment: { ...PAYMENT_OUTPUT, description: 'The retrieved payment object' },
metadata: {
type: 'json',
description: 'Payment summary metadata',
properties: PAYMENT_METADATA_OUTPUT_PROPERTIES,
},
},
}
+63
View File
@@ -0,0 +1,63 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { GetRefundParams, RefundResponse } from '@/tools/square/types'
import {
REFUND_METADATA_OUTPUT_PROPERTIES,
REFUND_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareGetRefundTool: ToolConfig<GetRefundParams, RefundResponse> = {
id: 'square_get_refund',
name: 'Square Get Refund',
description: 'Retrieve a single payment refund by its ID',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
refundId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the refund to retrieve',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/refunds/${encodeURIComponent(params.refundId)}`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const refund = data.refund ?? {}
return {
success: true,
output: {
refund,
metadata: {
id: refund.id,
status: refund.status ?? null,
payment_id: refund.payment_id ?? null,
},
},
}
},
outputs: {
refund: { ...REFUND_OUTPUT, description: 'The retrieved refund object' },
metadata: {
type: 'json',
description: 'Refund summary metadata',
properties: REFUND_METADATA_OUTPUT_PROPERTIES,
},
},
}
+34
View File
@@ -0,0 +1,34 @@
export { squareBatchRetrieveInventoryCountsTool } from '@/tools/square/batch_retrieve_inventory_counts'
export { squareCancelInvoiceTool } from '@/tools/square/cancel_invoice'
export { squareCancelPaymentTool } from '@/tools/square/cancel_payment'
export { squareCompletePaymentTool } from '@/tools/square/complete_payment'
export { squareCreateCatalogImageTool } from '@/tools/square/create_catalog_image'
export { squareCreateCustomerTool } from '@/tools/square/create_customer'
export { squareCreateInvoiceTool } from '@/tools/square/create_invoice'
export { squareCreateOrderTool } from '@/tools/square/create_order'
export { squareCreatePaymentTool } from '@/tools/square/create_payment'
export { squareDeleteCatalogObjectTool } from '@/tools/square/delete_catalog_object'
export { squareDeleteCustomerTool } from '@/tools/square/delete_customer'
export { squareDeleteInvoiceTool } from '@/tools/square/delete_invoice'
export { squareGetCatalogObjectTool } from '@/tools/square/get_catalog_object'
export { squareGetCustomerTool } from '@/tools/square/get_customer'
export { squareGetInvoiceTool } from '@/tools/square/get_invoice'
export { squareGetLocationTool } from '@/tools/square/get_location'
export { squareGetOrderTool } from '@/tools/square/get_order'
export { squareGetPaymentTool } from '@/tools/square/get_payment'
export { squareGetRefundTool } from '@/tools/square/get_refund'
export { squareListCatalogTool } from '@/tools/square/list_catalog'
export { squareListCustomersTool } from '@/tools/square/list_customers'
export { squareListInvoicesTool } from '@/tools/square/list_invoices'
export { squareListLocationsTool } from '@/tools/square/list_locations'
export { squareListPaymentsTool } from '@/tools/square/list_payments'
export { squareListRefundsTool } from '@/tools/square/list_refunds'
export { squarePayOrderTool } from '@/tools/square/pay_order'
export { squarePublishInvoiceTool } from '@/tools/square/publish_invoice'
export { squareRefundPaymentTool } from '@/tools/square/refund_payment'
export { squareSearchCatalogObjectsTool } from '@/tools/square/search_catalog_objects'
export { squareSearchCustomersTool } from '@/tools/square/search_customers'
export { squareSearchInvoicesTool } from '@/tools/square/search_invoices'
export { squareSearchOrdersTool } from '@/tools/square/search_orders'
export { squareUpdateCustomerTool } from '@/tools/square/update_customer'
export { squareUpsertCatalogObjectTool } from '@/tools/square/upsert_catalog_object'
+78
View File
@@ -0,0 +1,78 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CatalogListResponse, ListCatalogParams } from '@/tools/square/types'
import {
CATALOG_OBJECT_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListCatalogTool: ToolConfig<ListCatalogParams, CatalogListResponse> = {
id: 'square_list_catalog',
name: 'Square List Catalog',
description: 'List catalog objects, optionally filtered by type',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
types: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated catalog object types to return (e.g. ITEM,CATEGORY). Defaults to all top-level types',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/catalog/list`)
if (params.types) url.searchParams.append('types', params.types)
if (params.cursor) url.searchParams.append('cursor', params.cursor)
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const objects = data.objects ?? []
return {
success: true,
output: {
objects,
metadata: {
count: objects.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
objects: {
type: 'array',
description: 'Array of catalog objects',
items: CATALOG_OBJECT_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+91
View File
@@ -0,0 +1,91 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CustomerListResponse, ListCustomersParams } from '@/tools/square/types'
import {
CUSTOMER_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListCustomersTool: ToolConfig<ListCustomersParams, CustomerListResponse> = {
id: 'square_list_customers',
name: 'Square List Customers',
description: 'List customer profiles in the Square customer directory',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page (max 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
sortField: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Field to sort by (DEFAULT or CREATED_AT)',
},
sortOrder: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order (ASC or DESC)',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/customers`)
if (params.limit !== undefined) url.searchParams.append('limit', params.limit.toString())
if (params.cursor) url.searchParams.append('cursor', params.cursor)
if (params.sortField) url.searchParams.append('sort_field', params.sortField)
if (params.sortOrder) url.searchParams.append('sort_order', params.sortOrder)
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const customers = data.customers ?? []
return {
success: true,
output: {
customers,
metadata: {
count: customers.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
customers: {
type: 'array',
description: 'Array of customer objects',
items: CUSTOMER_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+84
View File
@@ -0,0 +1,84 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { InvoiceListResponse, ListInvoicesParams } from '@/tools/square/types'
import {
INVOICE_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListInvoicesTool: ToolConfig<ListInvoicesParams, InvoiceListResponse> = {
id: 'square_list_invoices',
name: 'Square List Invoices',
description: 'List invoices for a specific location',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the location to list invoices for',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/invoices`)
url.searchParams.append('location_id', params.locationId)
if (params.limit !== undefined) url.searchParams.append('limit', params.limit.toString())
if (params.cursor) url.searchParams.append('cursor', params.cursor)
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const invoices = data.invoices ?? []
return {
success: true,
output: {
invoices,
metadata: {
count: invoices.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
invoices: {
type: 'array',
description: 'Array of invoice objects',
items: INVOICE_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+56
View File
@@ -0,0 +1,56 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { ListLocationsParams, LocationListResponse } from '@/tools/square/types'
import { LOCATION_OUTPUT, SQUARE_BASE_URL, squareHeaders } from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListLocationsTool: ToolConfig<ListLocationsParams, LocationListResponse> = {
id: 'square_list_locations',
name: 'Square List Locations',
description: 'List all locations associated with the Square account',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/locations`,
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const locations = data.locations ?? []
return {
success: true,
output: {
locations,
metadata: {
count: locations.length,
},
},
}
},
outputs: {
locations: {
type: 'array',
description: 'Array of location objects',
items: LOCATION_OUTPUT,
},
metadata: {
type: 'json',
description: 'List metadata',
properties: {
count: { type: 'number', description: 'Number of locations returned' },
},
},
},
}
+98
View File
@@ -0,0 +1,98 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { ListPaymentsParams, PaymentListResponse } from '@/tools/square/types'
import {
LIST_METADATA_OUTPUT_PROPERTIES,
PAYMENT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListPaymentsTool: ToolConfig<ListPaymentsParams, PaymentListResponse> = {
id: 'square_list_payments',
name: 'Square List Payments',
description: 'List payments taken by the account, optionally filtered by location and time range',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter payments by location ID',
},
beginTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'RFC 3339 timestamp for the beginning of the reporting period',
},
endTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'RFC 3339 timestamp for the end of the reporting period',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/payments`)
if (params.locationId) url.searchParams.append('location_id', params.locationId)
if (params.beginTime) url.searchParams.append('begin_time', params.beginTime)
if (params.endTime) url.searchParams.append('end_time', params.endTime)
if (params.limit !== undefined) url.searchParams.append('limit', params.limit.toString())
if (params.cursor) url.searchParams.append('cursor', params.cursor)
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const payments = data.payments ?? []
return {
success: true,
output: {
payments,
metadata: {
count: payments.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
payments: {
type: 'array',
description: 'Array of payment objects',
items: PAYMENT_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+105
View File
@@ -0,0 +1,105 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { ListRefundsParams, RefundListResponse } from '@/tools/square/types'
import {
LIST_METADATA_OUTPUT_PROPERTIES,
REFUND_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareListRefundsTool: ToolConfig<ListRefundsParams, RefundListResponse> = {
id: 'square_list_refunds',
name: 'Square List Refunds',
description: 'List payment refunds, optionally filtered by location, status, and time range',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter refunds by location ID',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by refund status (PENDING, COMPLETED, REJECTED, or FAILED)',
},
beginTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'RFC 3339 timestamp for the beginning of the reporting period',
},
endTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'RFC 3339 timestamp for the end of the reporting period',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: (params) => {
const url = new URL(`${SQUARE_BASE_URL}/v2/refunds`)
if (params.locationId) url.searchParams.append('location_id', params.locationId)
if (params.status) url.searchParams.append('status', params.status)
if (params.beginTime) url.searchParams.append('begin_time', params.beginTime)
if (params.endTime) url.searchParams.append('end_time', params.endTime)
if (params.limit !== undefined) url.searchParams.append('limit', params.limit.toString())
if (params.cursor) url.searchParams.append('cursor', params.cursor)
return url.toString()
},
method: 'GET',
headers: (params) => squareHeaders(params.apiKey),
},
transformResponse: async (response) => {
const data = await response.json()
const refunds = data.refunds ?? []
return {
success: true,
output: {
refunds,
metadata: {
count: refunds.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
refunds: {
type: 'array',
description: 'Array of refund objects',
items: REFUND_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+90
View File
@@ -0,0 +1,90 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { OrderResponse, PayOrderParams } from '@/tools/square/types'
import {
ORDER_METADATA_OUTPUT_PROPERTIES,
ORDER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squarePayOrderTool: ToolConfig<PayOrderParams, OrderResponse> = {
id: 'square_pay_order',
name: 'Square Pay Order',
description: 'Pay for an order using one or more already-approved payments',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the order to pay for',
},
paymentIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'IDs of approved payments to apply to the order',
},
orderVersion: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Version of the order being paid (for optimistic concurrency)',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/orders/${encodeURIComponent(params.orderId)}/pay`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
idempotency_key: params.idempotencyKey || generateId(),
}
if (params.paymentIds) body.payment_ids = params.paymentIds
if (params.orderVersion !== undefined) body.order_version = params.orderVersion
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const order = data.order ?? {}
return {
success: true,
output: {
order,
metadata: {
id: order.id,
state: order.state ?? null,
location_id: order.location_id ?? null,
},
},
}
},
outputs: {
order: { ...ORDER_OUTPUT, description: 'The paid order object' },
metadata: {
type: 'json',
description: 'Order summary metadata',
properties: ORDER_METADATA_OUTPUT_PROPERTIES,
},
},
}
+81
View File
@@ -0,0 +1,81 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { InvoiceResponse, PublishInvoiceParams } from '@/tools/square/types'
import {
INVOICE_METADATA_OUTPUT_PROPERTIES,
INVOICE_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squarePublishInvoiceTool: ToolConfig<PublishInvoiceParams, InvoiceResponse> = {
id: 'square_publish_invoice',
name: 'Square Publish Invoice',
description: 'Publish a draft invoice so it is sent to the customer and becomes payable',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
invoiceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the invoice to publish',
},
version: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Current version of the invoice (use the version returned by Create Invoice)',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: (params) =>
`${SQUARE_BASE_URL}/v2/invoices/${encodeURIComponent(params.invoiceId)}/publish`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => ({
version: params.version,
idempotency_key: params.idempotencyKey || generateId(),
}),
},
transformResponse: async (response) => {
const data = await response.json()
const invoice = data.invoice ?? {}
return {
success: true,
output: {
invoice,
metadata: {
id: invoice.id,
status: invoice.status ?? null,
version: invoice.version ?? null,
},
},
}
},
outputs: {
invoice: { ...INVOICE_OUTPUT, description: 'The published invoice object' },
metadata: {
type: 'json',
description: 'Invoice summary metadata',
properties: INVOICE_METADATA_OUTPUT_PROPERTIES,
},
},
}
+97
View File
@@ -0,0 +1,97 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { RefundPaymentParams, RefundResponse } from '@/tools/square/types'
import {
REFUND_METADATA_OUTPUT_PROPERTIES,
REFUND_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareRefundPaymentTool: ToolConfig<RefundPaymentParams, RefundResponse> = {
id: 'square_refund_payment',
name: 'Square Refund Payment',
description: 'Refund all or part of a completed payment',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
paymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the payment to refund',
},
amount: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Amount to refund in the smallest currency denomination (e.g. 100 = $1.00)',
},
currency: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Three-letter ISO 4217 currency code (e.g. USD)',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
reason: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Reason for the refund',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/refunds`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
idempotency_key: params.idempotencyKey || generateId(),
payment_id: params.paymentId,
amount_money: { amount: params.amount, currency: params.currency },
}
if (params.reason) body.reason = params.reason
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const refund = data.refund ?? {}
return {
success: true,
output: {
refund,
metadata: {
id: refund.id,
status: refund.status ?? null,
payment_id: refund.payment_id ?? null,
},
},
}
},
outputs: {
refund: { ...REFUND_OUTPUT, description: 'The created refund object' },
metadata: {
type: 'json',
description: 'Refund summary metadata',
properties: REFUND_METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,96 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CatalogListResponse, SearchCatalogObjectsParams } from '@/tools/square/types'
import {
CATALOG_OBJECT_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareSearchCatalogObjectsTool: ToolConfig<
SearchCatalogObjectsParams,
CatalogListResponse
> = {
id: 'square_search_catalog_objects',
name: 'Square Search Catalog Objects',
description: 'Search catalog objects by type and query filters',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
objectTypes: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of catalog object types to search (e.g. ["ITEM","CATEGORY"])',
},
query: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Square catalog query object (e.g. {"text_query":{"keywords":["coffee"]}} or {"prefix_query":{...}})',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/catalog/search`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.objectTypes) body.object_types = params.objectTypes
if (params.query) body.query = params.query
if (params.limit !== undefined) body.limit = params.limit
if (params.cursor) body.cursor = params.cursor
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const objects = data.objects ?? []
return {
success: true,
output: {
objects,
metadata: {
count: objects.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
objects: {
type: 'array',
description: 'Array of matching catalog objects',
items: CATALOG_OBJECT_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+86
View File
@@ -0,0 +1,86 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CustomerListResponse, SearchCustomersParams } from '@/tools/square/types'
import {
CUSTOMER_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareSearchCustomersTool: ToolConfig<SearchCustomersParams, CustomerListResponse> = {
id: 'square_search_customers',
name: 'Square Search Customers',
description: 'Search customer profiles using filters such as email, phone, or creation date',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
query: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Square customer query object with optional filter and sort (e.g. {"filter":{"email_address":{"exact":"a@b.com"}}})',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/customers/search`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.query) body.query = params.query
if (params.limit !== undefined) body.limit = params.limit
if (params.cursor) body.cursor = params.cursor
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const customers = data.customers ?? []
return {
success: true,
output: {
customers,
metadata: {
count: customers.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
customers: {
type: 'array',
description: 'Array of matching customer objects',
items: CUSTOMER_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+88
View File
@@ -0,0 +1,88 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { InvoiceListResponse, SearchInvoicesParams } from '@/tools/square/types'
import {
INVOICE_OUTPUT,
LIST_METADATA_OUTPUT_PROPERTIES,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareSearchInvoicesTool: ToolConfig<SearchInvoicesParams, InvoiceListResponse> = {
id: 'square_search_invoices',
name: 'Square Search Invoices',
description: 'Search invoices across one or more locations',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the location to search within (Square allows one location per search)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/invoices/search`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
query: {
filter: { location_ids: [params.locationId] },
},
}
if (params.limit !== undefined) body.limit = params.limit
if (params.cursor) body.cursor = params.cursor
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const invoices = data.invoices ?? []
return {
success: true,
output: {
invoices,
metadata: {
count: invoices.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
invoices: {
type: 'array',
description: 'Array of matching invoice objects',
items: INVOICE_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
+94
View File
@@ -0,0 +1,94 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { OrderListResponse, SearchOrdersParams } from '@/tools/square/types'
import {
LIST_METADATA_OUTPUT_PROPERTIES,
ORDER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareSearchOrdersTool: ToolConfig<SearchOrdersParams, OrderListResponse> = {
id: 'square_search_orders',
name: 'Square Search Orders',
description: 'Search orders across one or more locations using filters and sorting',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
locationIds: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Array of location IDs to search within',
},
query: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Square order query object with optional filter and sort (e.g. {"filter":{"state_filter":{"states":["OPEN"]}}})',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return per page',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from a previous response',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/orders/search`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {
location_ids: params.locationIds,
}
if (params.query) body.query = params.query
if (params.limit !== undefined) body.limit = params.limit
if (params.cursor) body.cursor = params.cursor
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const orders = data.orders ?? []
return {
success: true,
output: {
orders,
metadata: {
count: orders.length,
cursor: data.cursor ?? null,
},
},
}
},
outputs: {
orders: {
type: 'array',
description: 'Array of matching order objects',
items: ORDER_OUTPUT,
},
metadata: {
type: 'json',
description: 'List pagination metadata',
properties: LIST_METADATA_OUTPUT_PROPERTIES,
},
},
}
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CustomerResponse, UpdateCustomerParams } from '@/tools/square/types'
import {
CUSTOMER_METADATA_OUTPUT_PROPERTIES,
CUSTOMER_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareUpdateCustomerTool: ToolConfig<UpdateCustomerParams, CustomerResponse> = {
id: 'square_update_customer',
name: 'Square Update Customer',
description: 'Update fields on an existing customer profile',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
customerId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the customer to update',
},
givenName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'First name of the customer',
},
familyName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Last name of the customer',
},
companyName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Business name of the customer',
},
nickname: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Nickname of the customer',
},
emailAddress: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Email address of the customer',
},
phoneNumber: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Phone number of the customer',
},
birthday: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Birthday in YYYY-MM-DD or MM-DD format',
},
note: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Note about the customer',
},
referenceId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional external reference for the customer',
},
address: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Square address object for the customer',
},
},
request: {
url: (params) => `${SQUARE_BASE_URL}/v2/customers/${encodeURIComponent(params.customerId)}`,
method: 'PUT',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.givenName !== undefined) body.given_name = params.givenName
if (params.familyName !== undefined) body.family_name = params.familyName
if (params.companyName !== undefined) body.company_name = params.companyName
if (params.nickname !== undefined) body.nickname = params.nickname
if (params.emailAddress !== undefined) body.email_address = params.emailAddress
if (params.phoneNumber !== undefined) body.phone_number = params.phoneNumber
if (params.birthday !== undefined) body.birthday = params.birthday
if (params.note !== undefined) body.note = params.note
if (params.referenceId !== undefined) body.reference_id = params.referenceId
if (params.address !== undefined) body.address = params.address
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const customer = data.customer ?? {}
return {
success: true,
output: {
customer,
metadata: {
id: customer.id,
email_address: customer.email_address ?? null,
given_name: customer.given_name ?? null,
family_name: customer.family_name ?? null,
},
},
}
},
outputs: {
customer: { ...CUSTOMER_OUTPUT, description: 'The updated customer object' },
metadata: {
type: 'json',
description: 'Customer summary metadata',
properties: CUSTOMER_METADATA_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,78 @@
import { generateId } from '@sim/utils/id'
import { ErrorExtractorId } from '@/tools/error-extractors'
import type { CatalogObjectResponse, UpsertCatalogObjectParams } from '@/tools/square/types'
import {
CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
CATALOG_OBJECT_OUTPUT,
SQUARE_BASE_URL,
squareHeaders,
} from '@/tools/square/types'
import type { ToolConfig } from '@/tools/types'
export const squareUpsertCatalogObjectTool: ToolConfig<
UpsertCatalogObjectParams,
CatalogObjectResponse
> = {
id: 'square_upsert_catalog_object',
name: 'Square Upsert Catalog Object',
description: 'Create or update a catalog object such as an item, variation, or category',
version: '1.0.0',
errorExtractor: ErrorExtractorId.SQUARE_ERRORS,
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Square access token (personal access token)',
},
object: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Square catalog object to create or update. Use ID "#name" for new objects (e.g. {"type":"ITEM","id":"#Coffee","item_data":{"name":"Coffee"}})',
},
idempotencyKey: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Unique key to make the request idempotent (auto-generated if omitted)',
},
},
request: {
url: () => `${SQUARE_BASE_URL}/v2/catalog/object`,
method: 'POST',
headers: (params) => squareHeaders(params.apiKey),
body: (params) => ({
idempotency_key: params.idempotencyKey || generateId(),
object: params.object,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const object = data.catalog_object ?? {}
return {
success: true,
output: {
object,
metadata: {
id: object.id,
type: object.type ?? null,
version: object.version ?? null,
},
},
}
},
outputs: {
object: { ...CATALOG_OBJECT_OUTPUT, description: 'The created or updated catalog object' },
metadata: {
type: 'json',
description: 'Catalog object summary metadata',
properties: CATALOG_OBJECT_METADATA_OUTPUT_PROPERTIES,
},
},
}
+2 -2
View File
@@ -9,8 +9,8 @@ const QUERY_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/queries')
const SELECTOR_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/selectors')
const BASELINE = {
totalRoutes: 826,
zodRoutes: 826,
totalRoutes: 827,
zodRoutes: 827,
nonZodRoutes: 0,
} as const