mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(loops): align integration with live API docs, add suppression + get-template tools (#5358)
* fix(loops): align integration with live API docs, add suppression + get-template tools - fix list_transactional_emails endpoint URL (was /transactional, now /transactional-emails) - fix response fields to match actual API schema (createdAt/updatedAt, not the never-existent lastUpdated) - add loops_check_contact_suppression, loops_remove_contact_suppression, loops_get_transactional_email tools - wire new tools into block operations, outputs, and registries - alphabetize tools/registry.ts loops entries * fix(loops): expose contactId output, fix stale tool description - add missing contactId block output for check_contact_suppression (Greptile P1) - fix list_transactional_emails description to mention createdAt (Greptile P2) * fix(loops): restore lastUpdated as backwards-compat alias on list_transactional_emails Final validation pass found that /api/v1/transactional (the endpoint this tool used before this PR) is a real, functional, deprecated Loops endpoint whose schema genuinely returns lastUpdated - it was not a broken/invented field. Migrating to /api/v1/transactional-emails is still correct (current endpoint, better error semantics), but dropping lastUpdated would break any existing workflow reading it from this block's output. Keep it as a deprecated alias of updatedAt alongside the new createdAt/updatedAt fields. * fix(loops): update id output description to cover get_transactional_email
This commit is contained in:
@@ -32,6 +32,9 @@ export const LoopsBlock: BlockConfig<LoopsResponse> = {
|
||||
{ label: 'List Transactional Emails', id: 'list_transactional_emails' },
|
||||
{ label: 'Create Contact Property', id: 'create_contact_property' },
|
||||
{ label: 'List Contact Properties', id: 'list_contact_properties' },
|
||||
{ label: 'Check Contact Suppression', id: 'check_contact_suppression' },
|
||||
{ label: 'Remove Contact Suppression', id: 'remove_contact_suppression' },
|
||||
{ label: 'Get Transactional Email', id: 'get_transactional_email' },
|
||||
],
|
||||
value: () => 'create_contact',
|
||||
},
|
||||
@@ -47,7 +50,7 @@ export const LoopsBlock: BlockConfig<LoopsResponse> = {
|
||||
value: ['create_contact', 'send_transactional_email'],
|
||||
},
|
||||
},
|
||||
// Optional email for update, find, delete, send event
|
||||
// Optional email for update, find, delete, send event, suppression lookups
|
||||
{
|
||||
id: 'contactEmail',
|
||||
title: 'Email',
|
||||
@@ -55,7 +58,14 @@ export const LoopsBlock: BlockConfig<LoopsResponse> = {
|
||||
placeholder: 'Enter email address',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['update_contact', 'find_contact', 'delete_contact', 'send_event'],
|
||||
value: [
|
||||
'update_contact',
|
||||
'find_contact',
|
||||
'delete_contact',
|
||||
'send_event',
|
||||
'check_contact_suppression',
|
||||
'remove_contact_suppression',
|
||||
],
|
||||
},
|
||||
},
|
||||
// User ID for operations that support it
|
||||
@@ -66,7 +76,14 @@ export const LoopsBlock: BlockConfig<LoopsResponse> = {
|
||||
placeholder: 'Enter user ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['update_contact', 'find_contact', 'delete_contact', 'send_event'],
|
||||
value: [
|
||||
'update_contact',
|
||||
'find_contact',
|
||||
'delete_contact',
|
||||
'send_event',
|
||||
'check_contact_suppression',
|
||||
'remove_contact_suppression',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Contact fields
|
||||
@@ -199,10 +216,13 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
title: 'Transactional Email ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter template ID (e.g., clx...)',
|
||||
required: { field: 'operation', value: 'send_transactional_email' },
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['send_transactional_email', 'get_transactional_email'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'send_transactional_email',
|
||||
value: ['send_transactional_email', 'get_transactional_email'],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -426,6 +446,9 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
'loops_list_transactional_emails',
|
||||
'loops_create_contact_property',
|
||||
'loops_list_contact_properties',
|
||||
'loops_check_contact_suppression',
|
||||
'loops_remove_contact_suppression',
|
||||
'loops_get_transactional_email',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `loops_${params.operation}`,
|
||||
@@ -497,6 +520,16 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
case 'list_contact_properties':
|
||||
if (params.propertyFilter) result.list = params.propertyFilter
|
||||
break
|
||||
|
||||
case 'check_contact_suppression':
|
||||
case 'remove_contact_suppression':
|
||||
if (params.contactEmail) result.email = params.contactEmail
|
||||
if (params.userId) result.userId = params.userId
|
||||
break
|
||||
|
||||
case 'get_transactional_email':
|
||||
result.transactionalId = params.transactionalId
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -531,7 +564,10 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the operation succeeded' },
|
||||
id: { type: 'string', description: 'Contact ID (create/update operations)' },
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Contact ID (create/update operations) or template ID (get transactional email)',
|
||||
},
|
||||
contacts: {
|
||||
type: 'json',
|
||||
description:
|
||||
@@ -544,7 +580,8 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
},
|
||||
transactionalEmails: {
|
||||
type: 'json',
|
||||
description: 'Array of transactional email templates (id, name, lastUpdated, dataVariables)',
|
||||
description:
|
||||
'Array of transactional email templates (id, name, createdAt, updatedAt, lastUpdated (deprecated alias of updatedAt), dataVariables)',
|
||||
},
|
||||
pagination: {
|
||||
type: 'json',
|
||||
@@ -555,6 +592,50 @@ Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
type: 'json',
|
||||
description: 'Array of contact properties (key, label, type)',
|
||||
},
|
||||
isSuppressed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the contact is on the suppression list (check suppression)',
|
||||
},
|
||||
contactId: {
|
||||
type: 'string',
|
||||
description: 'The Loops-assigned contact ID (check suppression)',
|
||||
},
|
||||
removalQuotaLimit: {
|
||||
type: 'number',
|
||||
description: 'Total suppression-removal quota for the team',
|
||||
},
|
||||
removalQuotaRemaining: {
|
||||
type: 'number',
|
||||
description: 'Remaining suppression-removal quota for the team',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Transactional email template name (get transactional email)',
|
||||
},
|
||||
draftEmailMessageId: {
|
||||
type: 'string',
|
||||
description: 'ID of the draft email message, if any (get transactional email)',
|
||||
},
|
||||
publishedEmailMessageId: {
|
||||
type: 'string',
|
||||
description: 'ID of the published email message, if any (get transactional email)',
|
||||
},
|
||||
transactionalGroupId: {
|
||||
type: 'string',
|
||||
description: 'ID of the transactional group, if any (get transactional email)',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
description: 'Creation timestamp (get transactional email)',
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string',
|
||||
description: 'Last updated timestamp (get transactional email)',
|
||||
},
|
||||
dataVariables: {
|
||||
type: 'json',
|
||||
description: 'Template data variable names (get transactional email)',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -655,5 +736,12 @@ export const LoopsBlockMeta = {
|
||||
content:
|
||||
'# Send Transactional Email\n\nDeliver a templated transactional email through Loops.\n\n## Steps\n1. Confirm the transactional email template ID to use.\n2. Build the data variables JSON to match the variable names in the template, such as name and a confirmation URL.\n3. Send Transactional Email with the recipient email, template ID, and data variables, attaching files if needed.\n\n## Output\nConfirmation of send success and the template ID and recipient used.',
|
||||
},
|
||||
{
|
||||
name: 'manage-suppression-compliance',
|
||||
description:
|
||||
'Check and clear Loops suppression status for a contact to keep deliverability and unsubscribe compliance in check.',
|
||||
content:
|
||||
'# Manage Suppression Compliance\n\nKeep Loops sending compliant and deliverable.\n\n## Steps\n1. Check Contact Suppression by email or user ID to see if the contact bounced, complained, or unsubscribed.\n2. If the contact should be re-enabled (e.g. a confirmed re-opt-in), Remove Contact Suppression for the same identifier, noting the remaining removal quota.\n3. Log the result so support and compliance workflows have an audit trail.\n\n## Output\nThe suppression status before and after the change, plus the remaining removal quota.',
|
||||
},
|
||||
],
|
||||
} as const satisfies BlockMeta
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import type {
|
||||
LoopsCheckContactSuppressionParams,
|
||||
LoopsCheckContactSuppressionResponse,
|
||||
} from '@/tools/loops/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const loopsCheckContactSuppressionTool: ToolConfig<
|
||||
LoopsCheckContactSuppressionParams,
|
||||
LoopsCheckContactSuppressionResponse
|
||||
> = {
|
||||
id: 'loops_check_contact_suppression',
|
||||
name: 'Loops Check Contact Suppression',
|
||||
description:
|
||||
'Check whether a Loops contact is on the suppression list (bounced, complained, or unsubscribed) by email address or userId.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Loops API key for authentication',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The contact email address to check (at least one of email or userId is required)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The contact userId to check (at least one of email or userId is required)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.email && !params.userId) {
|
||||
throw new Error('At least one of email or userId is required to check suppression status')
|
||||
}
|
||||
const base = 'https://app.loops.so/api/v1/contacts/suppression'
|
||||
if (params.email) return `${base}?email=${encodeURIComponent(params.email.trim())}`
|
||||
return `${base}?userId=${encodeURIComponent(params.userId!.trim())}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.isSuppressed == null) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
contactId: null,
|
||||
email: null,
|
||||
userId: null,
|
||||
isSuppressed: false,
|
||||
removalQuotaLimit: null,
|
||||
removalQuotaRemaining: null,
|
||||
},
|
||||
error: data.message ?? 'Failed to check contact suppression status',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contactId: (data.contact?.id as string) ?? null,
|
||||
email: (data.contact?.email as string) ?? null,
|
||||
userId: (data.contact?.userId as string) ?? null,
|
||||
isSuppressed: (data.isSuppressed as boolean) ?? false,
|
||||
removalQuotaLimit: (data.removalQuota?.limit as number) ?? null,
|
||||
removalQuotaRemaining: (data.removalQuota?.remaining as number) ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contactId: { type: 'string', description: 'The Loops-assigned contact ID', optional: true },
|
||||
email: { type: 'string', description: 'The contact email address', optional: true },
|
||||
userId: { type: 'string', description: 'The contact userId', optional: true },
|
||||
isSuppressed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the contact is on the suppression list',
|
||||
},
|
||||
removalQuotaLimit: {
|
||||
type: 'number',
|
||||
description: 'Total suppression-removal quota for the team',
|
||||
optional: true,
|
||||
},
|
||||
removalQuotaRemaining: {
|
||||
type: 'number',
|
||||
description: 'Remaining suppression-removal quota for the team',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import type {
|
||||
LoopsGetTransactionalEmailParams,
|
||||
LoopsGetTransactionalEmailResponse,
|
||||
} from '@/tools/loops/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const loopsGetTransactionalEmailTool: ToolConfig<
|
||||
LoopsGetTransactionalEmailParams,
|
||||
LoopsGetTransactionalEmailResponse
|
||||
> = {
|
||||
id: 'loops_get_transactional_email',
|
||||
name: 'Loops Get Transactional Email',
|
||||
description:
|
||||
'Retrieve a single transactional email template from your Loops account by its ID, including its data variables and draft/published message IDs.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Loops API key for authentication',
|
||||
},
|
||||
transactionalId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the transactional email template to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://app.loops.so/api/v1/transactional-emails/${encodeURIComponent(params.transactionalId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.id) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: null,
|
||||
name: null,
|
||||
draftEmailMessageId: null,
|
||||
publishedEmailMessageId: null,
|
||||
transactionalGroupId: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
dataVariables: [],
|
||||
},
|
||||
error: data.message ?? 'Failed to get transactional email',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: (data.id as string) ?? null,
|
||||
name: (data.name as string) ?? null,
|
||||
draftEmailMessageId: (data.draftEmailMessageId as string) ?? null,
|
||||
publishedEmailMessageId: (data.publishedEmailMessageId as string) ?? null,
|
||||
transactionalGroupId: (data.transactionalGroupId as string) ?? null,
|
||||
createdAt: (data.createdAt as string) ?? null,
|
||||
updatedAt: (data.updatedAt as string) ?? null,
|
||||
dataVariables: (data.dataVariables as string[]) ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'The transactional email template ID', optional: true },
|
||||
name: { type: 'string', description: 'The template name', optional: true },
|
||||
draftEmailMessageId: {
|
||||
type: 'string',
|
||||
description: 'ID of the draft email message, if any',
|
||||
optional: true,
|
||||
},
|
||||
publishedEmailMessageId: {
|
||||
type: 'string',
|
||||
description: 'ID of the published email message, if any',
|
||||
optional: true,
|
||||
},
|
||||
transactionalGroupId: {
|
||||
type: 'string',
|
||||
description: 'ID of the transactional group this template belongs to, if any',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)', optional: true },
|
||||
updatedAt: {
|
||||
type: 'string',
|
||||
description: 'Last updated timestamp (ISO 8601)',
|
||||
optional: true,
|
||||
},
|
||||
dataVariables: {
|
||||
type: 'array',
|
||||
description: 'Template data variable names',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
export { loopsCheckContactSuppressionTool } from '@/tools/loops/check_contact_suppression'
|
||||
export { loopsCreateContactTool } from '@/tools/loops/create_contact'
|
||||
export { loopsCreateContactPropertyTool } from '@/tools/loops/create_contact_property'
|
||||
export { loopsDeleteContactTool } from '@/tools/loops/delete_contact'
|
||||
export { loopsFindContactTool } from '@/tools/loops/find_contact'
|
||||
export { loopsGetTransactionalEmailTool } from '@/tools/loops/get_transactional_email'
|
||||
export { loopsListContactPropertiesTool } from '@/tools/loops/list_contact_properties'
|
||||
export { loopsListMailingListsTool } from '@/tools/loops/list_mailing_lists'
|
||||
export { loopsListTransactionalEmailsTool } from '@/tools/loops/list_transactional_emails'
|
||||
export { loopsRemoveContactSuppressionTool } from '@/tools/loops/remove_contact_suppression'
|
||||
export { loopsSendEventTool } from '@/tools/loops/send_event'
|
||||
export { loopsSendTransactionalEmailTool } from '@/tools/loops/send_transactional_email'
|
||||
export { loopsUpdateContactTool } from '@/tools/loops/update_contact'
|
||||
|
||||
@@ -11,7 +11,7 @@ export const loopsListTransactionalEmailsTool: ToolConfig<
|
||||
id: 'loops_list_transactional_emails',
|
||||
name: 'Loops List Transactional Emails',
|
||||
description:
|
||||
'Retrieve a list of published transactional email templates from your Loops account. Returns each template with its ID, name, last updated timestamp, and data variables.',
|
||||
'Retrieve a list of published transactional email templates from your Loops account. Returns each template with its ID, name, created/updated timestamps, and data variables.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
@@ -37,7 +37,7 @@ export const loopsListTransactionalEmailsTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = 'https://app.loops.so/api/v1/transactional'
|
||||
const base = 'https://app.loops.so/api/v1/transactional-emails'
|
||||
const queryParams: string[] = []
|
||||
if (params.perPage) queryParams.push(`perPage=${encodeURIComponent(params.perPage)}`)
|
||||
if (params.cursor) queryParams.push(`cursor=${encodeURIComponent(params.cursor)}`)
|
||||
@@ -78,7 +78,11 @@ export const loopsListTransactionalEmailsTool: ToolConfig<
|
||||
transactionalEmails: emails.map((email: Record<string, unknown>) => ({
|
||||
id: (email.id as string) ?? '',
|
||||
name: (email.name as string) ?? '',
|
||||
lastUpdated: (email.lastUpdated as string) ?? '',
|
||||
createdAt: (email.createdAt as string) ?? '',
|
||||
updatedAt: (email.updatedAt as string) ?? '',
|
||||
// Deprecated alias of updatedAt, kept for backwards compatibility with the old
|
||||
// (now-removed) /api/v1/transactional list endpoint, which returned this field.
|
||||
lastUpdated: (email.updatedAt as string) ?? '',
|
||||
dataVariables: (email.dataVariables as string[]) ?? [],
|
||||
})),
|
||||
pagination: {
|
||||
@@ -102,7 +106,12 @@ export const loopsListTransactionalEmailsTool: ToolConfig<
|
||||
properties: {
|
||||
id: { type: 'string', description: 'The transactional email template ID' },
|
||||
name: { type: 'string', description: 'The template name' },
|
||||
lastUpdated: { type: 'string', description: 'Last updated timestamp' },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp (ISO 8601)' },
|
||||
updatedAt: { type: 'string', description: 'Last updated timestamp (ISO 8601)' },
|
||||
lastUpdated: {
|
||||
type: 'string',
|
||||
description: 'Deprecated alias of updatedAt, kept for backwards compatibility',
|
||||
},
|
||||
dataVariables: {
|
||||
type: 'array',
|
||||
description: 'Template data variable names',
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import type {
|
||||
LoopsRemoveContactSuppressionParams,
|
||||
LoopsRemoveContactSuppressionResponse,
|
||||
} from '@/tools/loops/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const loopsRemoveContactSuppressionTool: ToolConfig<
|
||||
LoopsRemoveContactSuppressionParams,
|
||||
LoopsRemoveContactSuppressionResponse
|
||||
> = {
|
||||
id: 'loops_remove_contact_suppression',
|
||||
name: 'Loops Remove Contact Suppression',
|
||||
description:
|
||||
'Remove a Loops contact from the suppression list by email address or userId, allowing them to receive emails again. Subject to a team removal quota.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Loops API key for authentication',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The contact email address to remove from suppression (at least one of email or userId is required)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The contact userId to remove from suppression (at least one of email or userId is required)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.email && !params.userId) {
|
||||
throw new Error('At least one of email or userId is required to remove suppression')
|
||||
}
|
||||
const base = 'https://app.loops.so/api/v1/contacts/suppression'
|
||||
if (params.email) return `${base}?email=${encodeURIComponent(params.email.trim())}`
|
||||
return `${base}?userId=${encodeURIComponent(params.userId!.trim())}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
message: data.message ?? 'Failed to remove contact suppression',
|
||||
removalQuotaLimit: (data.removalQuota?.limit as number) ?? null,
|
||||
removalQuotaRemaining: (data.removalQuota?.remaining as number) ?? null,
|
||||
},
|
||||
error: data.message ?? 'Failed to remove contact suppression',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
message: data.message ?? 'Contact removed from suppression list.',
|
||||
removalQuotaLimit: (data.removalQuota?.limit as number) ?? null,
|
||||
removalQuotaRemaining: (data.removalQuota?.remaining as number) ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the contact was removed from suppression successfully',
|
||||
},
|
||||
message: { type: 'string', description: 'Status message from the API', optional: true },
|
||||
removalQuotaLimit: {
|
||||
type: 'number',
|
||||
description: 'Total suppression-removal quota for the team',
|
||||
optional: true,
|
||||
},
|
||||
removalQuotaRemaining: {
|
||||
type: 'number',
|
||||
description: 'Remaining suppression-removal quota for the team',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -70,6 +70,20 @@ export interface LoopsListContactPropertiesParams extends LoopsBaseParams {
|
||||
list?: string
|
||||
}
|
||||
|
||||
export interface LoopsCheckContactSuppressionParams extends LoopsBaseParams {
|
||||
email?: string
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface LoopsRemoveContactSuppressionParams extends LoopsBaseParams {
|
||||
email?: string
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface LoopsGetTransactionalEmailParams extends LoopsBaseParams {
|
||||
transactionalId: string
|
||||
}
|
||||
|
||||
interface LoopsContact {
|
||||
id: string
|
||||
email: string
|
||||
@@ -138,6 +152,9 @@ export interface LoopsListTransactionalEmailsResponse extends ToolResponse {
|
||||
transactionalEmails: {
|
||||
id: string
|
||||
name: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
/** @deprecated Alias of updatedAt, kept for backwards compatibility */
|
||||
lastUpdated: string
|
||||
dataVariables: string[]
|
||||
}[]
|
||||
@@ -168,6 +185,39 @@ export interface LoopsListContactPropertiesResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface LoopsCheckContactSuppressionResponse extends ToolResponse {
|
||||
output: {
|
||||
contactId: string | null
|
||||
email: string | null
|
||||
userId: string | null
|
||||
isSuppressed: boolean
|
||||
removalQuotaLimit: number | null
|
||||
removalQuotaRemaining: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface LoopsRemoveContactSuppressionResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string | null
|
||||
removalQuotaLimit: number | null
|
||||
removalQuotaRemaining: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface LoopsGetTransactionalEmailResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string | null
|
||||
name: string | null
|
||||
draftEmailMessageId: string | null
|
||||
publishedEmailMessageId: string | null
|
||||
transactionalGroupId: string | null
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
dataVariables: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type LoopsResponse =
|
||||
| LoopsCreateContactResponse
|
||||
| LoopsUpdateContactResponse
|
||||
@@ -179,6 +229,9 @@ export type LoopsResponse =
|
||||
| LoopsListTransactionalEmailsResponse
|
||||
| LoopsCreateContactPropertyResponse
|
||||
| LoopsListContactPropertiesResponse
|
||||
| LoopsCheckContactSuppressionResponse
|
||||
| LoopsRemoveContactSuppressionResponse
|
||||
| LoopsGetTransactionalEmailResponse
|
||||
|
||||
export const LOOPS_CONTACT_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string' as const, description: 'Loops-assigned contact ID' },
|
||||
|
||||
@@ -2065,13 +2065,16 @@ import {
|
||||
logsQueryTool,
|
||||
} from '@/tools/logs'
|
||||
import {
|
||||
loopsCheckContactSuppressionTool,
|
||||
loopsCreateContactPropertyTool,
|
||||
loopsCreateContactTool,
|
||||
loopsDeleteContactTool,
|
||||
loopsFindContactTool,
|
||||
loopsGetTransactionalEmailTool,
|
||||
loopsListContactPropertiesTool,
|
||||
loopsListMailingListsTool,
|
||||
loopsListTransactionalEmailsTool,
|
||||
loopsRemoveContactSuppressionTool,
|
||||
loopsSendEventTool,
|
||||
loopsSendTransactionalEmailTool,
|
||||
loopsUpdateContactTool,
|
||||
@@ -4633,16 +4636,19 @@ export const tools: Record<string, ToolConfig> = {
|
||||
logs_get: logsGetTool,
|
||||
logs_get_execution: logsGetExecutionTool,
|
||||
logs_get_run_details: logsGetRunDetailsTool,
|
||||
loops_check_contact_suppression: loopsCheckContactSuppressionTool,
|
||||
loops_create_contact: loopsCreateContactTool,
|
||||
loops_create_contact_property: loopsCreateContactPropertyTool,
|
||||
loops_update_contact: loopsUpdateContactTool,
|
||||
loops_find_contact: loopsFindContactTool,
|
||||
loops_delete_contact: loopsDeleteContactTool,
|
||||
loops_find_contact: loopsFindContactTool,
|
||||
loops_get_transactional_email: loopsGetTransactionalEmailTool,
|
||||
loops_list_contact_properties: loopsListContactPropertiesTool,
|
||||
loops_list_mailing_lists: loopsListMailingListsTool,
|
||||
loops_list_transactional_emails: loopsListTransactionalEmailsTool,
|
||||
loops_send_transactional_email: loopsSendTransactionalEmailTool,
|
||||
loops_remove_contact_suppression: loopsRemoveContactSuppressionTool,
|
||||
loops_send_event: loopsSendEventTool,
|
||||
loops_send_transactional_email: loopsSendTransactionalEmailTool,
|
||||
loops_update_contact: loopsUpdateContactTool,
|
||||
luma_add_guests: lumaAddGuestsTool,
|
||||
luma_cancel_event: lumaCancelEventTool,
|
||||
luma_create_event: lumaCreateEventTool,
|
||||
|
||||
Reference in New Issue
Block a user