From c90cab8729234848e5a41bf44cf7f336cfde9ae7 Mon Sep 17 00:00:00 2001 From: Odai Ahmad Date: Sun, 16 Aug 2026 18:20:50 +0300 Subject: [PATCH] feat(pieces): add output schemas to IMAP, WordPress, Resend and Google Contacts (#14837) --- .../community/google-contacts/package.json | 2 +- .../src/lib/action/create-contact.ts | 2 + .../src/lib/action/search-contact.ts | 2 + .../src/lib/action/update-contact.ts | 2 + .../google-contacts/src/lib/output-schemas.ts | 134 +++++++++++ .../src/lib/trigger/new-contact.ts | 2 + packages/pieces/community/imap/package.json | 2 +- .../imap/src/lib/actions/copy-email.ts | 2 + .../imap/src/lib/actions/move-email.ts | 2 + .../community/imap/src/lib/output-schemas.ts | 69 ++++++ .../imap/src/lib/triggers/new-email.ts | 2 + packages/pieces/community/resend/package.json | 2 +- .../actions/cancel-scheduled-email.action.ts | 2 + .../src/lib/actions/create-audience.action.ts | 2 + .../lib/actions/create-broadcast.action.ts | 2 + .../src/lib/actions/create-contact.action.ts | 2 + .../src/lib/actions/create-domain.action.ts | 2 + .../src/lib/actions/delete-audience.action.ts | 2 + .../lib/actions/delete-broadcast.action.ts | 2 + .../src/lib/actions/delete-contact.action.ts | 2 + .../src/lib/actions/delete-domain.action.ts | 2 + .../lib/actions/get-email-status.action.ts | 2 + .../src/lib/actions/list-audiences.action.ts | 2 + .../src/lib/actions/list-broadcasts.action.ts | 2 + .../src/lib/actions/list-contacts.action.ts | 2 + .../src/lib/actions/list-domains.action.ts | 2 + .../src/lib/actions/list-emails.action.ts | 2 + .../lib/actions/reschedule-email.action.ts | 2 + .../lib/actions/send-batch-emails.action.ts | 2 + .../src/lib/actions/send-broadcast.action.ts | 2 + .../resend/src/lib/actions/send-email.ts | 2 + .../src/lib/actions/update-contact.action.ts | 2 + .../src/lib/actions/verify-domain.action.ts | 2 + .../resend/src/lib/output-schemas.ts | 212 ++++++++++++++++++ .../pieces/community/wordpress/package.json | 2 +- .../src/lib/actions/create-page.action.ts | 2 + .../src/lib/actions/create-post.action.ts | 2 + .../src/lib/actions/get-post.action.ts | 2 + .../src/lib/actions/update-post.action.ts | 2 + .../wordpress/src/lib/output-schemas.ts | 148 ++++++++++++ .../src/lib/trigger/new-post.trigger.ts | 2 + 41 files changed, 633 insertions(+), 4 deletions(-) create mode 100644 packages/pieces/community/google-contacts/src/lib/output-schemas.ts create mode 100644 packages/pieces/community/imap/src/lib/output-schemas.ts create mode 100644 packages/pieces/community/resend/src/lib/output-schemas.ts create mode 100644 packages/pieces/community/wordpress/src/lib/output-schemas.ts diff --git a/packages/pieces/community/google-contacts/package.json b/packages/pieces/community/google-contacts/package.json index 1d8c700efa58..879a15093f30 100644 --- a/packages/pieces/community/google-contacts/package.json +++ b/packages/pieces/community/google-contacts/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-google-contacts", - "version": "0.4.8", + "version": "0.4.9", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "dependencies": { diff --git a/packages/pieces/community/google-contacts/src/lib/action/create-contact.ts b/packages/pieces/community/google-contacts/src/lib/action/create-contact.ts index 2808b3f417be..c9c480ee478c 100644 --- a/packages/pieces/community/google-contacts/src/lib/action/create-contact.ts +++ b/packages/pieces/community/google-contacts/src/lib/action/create-contact.ts @@ -7,6 +7,7 @@ import { } from '@activepieces/pieces-common'; import { googleContactsCommon } from '../common'; import { googleContactsAuth } from '../auth'; +import { addContactOutputSchema } from '../output-schemas'; export const googleContactsAddContactAction = createAction({ auth: googleContactsAuth, @@ -15,6 +16,7 @@ export const googleContactsAddContactAction = createAction({ audience: 'both', aiMetadata: { description: 'Creates a new person in the authenticated Google Contacts account from name, email, phone, company, and job title fields. Use when an agent needs to add someone to address book contacts; first and last name are required. Not idempotent — each call creates a separate contact even with identical input, so guard against duplicates.', idempotent: false }, displayName: 'Add Contact', + outputSchema: addContactOutputSchema, props: { firstName: Property.ShortText({ displayName: 'First Name', diff --git a/packages/pieces/community/google-contacts/src/lib/action/search-contact.ts b/packages/pieces/community/google-contacts/src/lib/action/search-contact.ts index f10363f2659b..f15c2c3690f7 100644 --- a/packages/pieces/community/google-contacts/src/lib/action/search-contact.ts +++ b/packages/pieces/community/google-contacts/src/lib/action/search-contact.ts @@ -8,6 +8,7 @@ import { } from '@activepieces/pieces-common'; import { googleContactsCommon } from '../common'; import { googleContactsAuth } from '../auth'; +import { searchContactsOutputSchema } from '../output-schemas'; export const googleContactsSearchContactsAction = createAction({ auth: googleContactsAuth, @@ -16,6 +17,7 @@ export const googleContactsSearchContactsAction = createAction({ audience: 'both', aiMetadata: { description: 'Searches the authenticated Google Contacts account for people matching a plain-text query, which prefix-matches against contact fields. Use when an agent needs to find a contact or its resourceName before updating it; the query is required and a read mask selects which fields come back (max 30 results). Read-only and idempotent.', idempotent: true }, displayName: 'Search Contacts', + outputSchema: searchContactsOutputSchema, props: { query: Property.ShortText({ displayName: 'Query', diff --git a/packages/pieces/community/google-contacts/src/lib/action/update-contact.ts b/packages/pieces/community/google-contacts/src/lib/action/update-contact.ts index 873cfeda1230..44093ebb400e 100644 --- a/packages/pieces/community/google-contacts/src/lib/action/update-contact.ts +++ b/packages/pieces/community/google-contacts/src/lib/action/update-contact.ts @@ -11,6 +11,7 @@ import { } from '@activepieces/pieces-common'; import { googleContactsCommon } from '../common'; import { googleContactsAuth } from '../auth'; +import { updateContactOutputSchema } from '../output-schemas'; export const googleContactsUpdateContactAction = createAction({ auth: googleContactsAuth, @@ -19,6 +20,7 @@ export const googleContactsUpdateContactAction = createAction({ audience: 'both', aiMetadata: { description: 'Updates fields (name, email, phone, company, job title) on an existing Google Contacts person identified by its resourceName (people/{id}). Use when an agent needs to modify a known contact; requires both the resourceName and the current etag, and a field mask listing which fields to update — the call is rejected if the contact changed since the etag was fetched. Repeating the same update with the same etag/values is safe and yields the same result.', idempotent: true }, displayName: 'Update Contact', + outputSchema: updateContactOutputSchema, props: { resourceName: Property.ShortText({ displayName: 'Resource Name', diff --git a/packages/pieces/community/google-contacts/src/lib/output-schemas.ts b/packages/pieces/community/google-contacts/src/lib/output-schemas.ts new file mode 100644 index 000000000000..7576a5c92571 --- /dev/null +++ b/packages/pieces/community/google-contacts/src/lib/output-schemas.ts @@ -0,0 +1,134 @@ +import { OutputSchema } from '@activepieces/pieces-framework'; + +// Google People returns every contact detail as a repeated field: a contact can hold several +// names, emails, phone numbers and organizations, each carrying its own `metadata`. Only the +// `metadata.primary` flag is surfaced from that block -- the rest (source ids, per-field etags) +// is server bookkeeping that says nothing about the contact. +const nameFields: OutputSchema['fields'] = [ + { key: 'displayName', label: 'Display Name' }, + { key: 'givenName', label: 'First Name' }, + { key: 'middleName', label: 'Middle Name' }, + { key: 'familyName', label: 'Last Name' }, + { key: 'displayNameLastFirst', label: 'Display Name (Last, First)' }, + { key: 'unstructuredName', label: 'Unstructured Name' }, + { key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' }, +]; + +const emailFields: OutputSchema['fields'] = [ + { key: 'value', label: 'Email Address', format: 'email' }, + { key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' }, +]; + +const phoneFields: OutputSchema['fields'] = [ + { key: 'value', label: 'Phone Number' }, + // Google normalises to E.164 here; the raw `value` keeps whatever the user typed. + { key: 'canonicalForm', label: 'Canonical Form' }, + { key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' }, +]; + +const organizationFields: OutputSchema['fields'] = [ + { key: 'name', label: 'Company' }, + { key: 'title', label: 'Job Title' }, + { key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' }, +]; + +const photoFields: OutputSchema['fields'] = [ + { key: 'url', label: 'Photo URL', format: 'image' }, + // True when this is Google's generated placeholder rather than a photo the contact has. + { key: 'default', label: 'Is Placeholder', format: 'boolean' }, + { key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' }, +]; + +const membershipFields: OutputSchema['fields'] = [ + { + key: 'contactGroupId', + label: 'Contact Group ID', + value: 'contactGroupMembership.contactGroupId', + }, + { + key: 'contactGroupResourceName', + label: 'Contact Group Resource Name', + value: 'contactGroupMembership.contactGroupResourceName', + }, +]; + +const metadataFields: OutputSchema['fields'] = [ + { key: 'objectType', label: 'Object Type' }, + { + key: 'sources', + label: 'Sources', + labelKey: 'updateTime', + listItems: [ + { key: 'type', label: 'Source Type' }, + { key: 'id', label: 'Source ID' }, + // The only timestamp on a contact, and what the polling trigger orders and dedupes on. + { key: 'updateTime', label: 'Last Updated', format: 'datetime' }, + ], + }, +]; + +// One person record. Shared by every action and the trigger, because People returns the same +// shape whether the contact was just created, just updated, matched by a search, or polled. +const personFields: OutputSchema['fields'] = [ + // `people/{person_id}` -- the id Update Contact requires. + { key: 'resourceName', label: 'Resource Name' }, + // Required alongside resourceName on update; the call is rejected if the contact moved on. + { key: 'etag', label: 'ETag' }, + { key: 'names', label: 'Names', labelKey: 'displayName', listItems: nameFields }, + { + key: 'emailAddresses', + label: 'Email Addresses', + labelKey: 'value', + listItems: emailFields, + }, + { + key: 'phoneNumbers', + label: 'Phone Numbers', + labelKey: 'value', + listItems: phoneFields, + }, + { + key: 'organizations', + label: 'Organizations', + labelKey: 'name', + listItems: organizationFields, + }, + { key: 'photos', label: 'Photos', labelKey: 'url', listItems: photoFields }, + { + key: 'memberships', + label: 'Group Memberships', + // labelKey resolves by dot path against the raw item, so it has to name the nested + // property rather than the flattened key used in membershipFields. + labelKey: 'contactGroupMembership.contactGroupId', + listItems: membershipFields, + }, + { key: 'metadata', label: 'Metadata', children: metadataFields }, +]; + +export const addContactOutputSchema: OutputSchema = { + fields: personFields, +}; + +export const updateContactOutputSchema: OutputSchema = { + fields: personFields, +}; + +// searchContacts wraps each hit as `{ person: {...} }` under a `results` array, so the person +// fields sit one level deeper than they do on the other three steps. Which of them are actually +// populated follows the caller's Read Mask -- the default is names and email addresses only. +export const searchContactsOutputSchema: OutputSchema = { + fields: [ + { + key: 'results', + label: 'Matching Contacts', + listItems: [ + { key: 'person', label: 'Contact', children: personFields }, + ], + }, + ], +}; + +// The polling trigger emits one person per run, so its payload is a person record directly. +export const newOrUpdatedContactOutputSchema: OutputSchema = { + fields: personFields, +}; diff --git a/packages/pieces/community/google-contacts/src/lib/trigger/new-contact.ts b/packages/pieces/community/google-contacts/src/lib/trigger/new-contact.ts index a340aa3077b6..62cf876e55bf 100644 --- a/packages/pieces/community/google-contacts/src/lib/trigger/new-contact.ts +++ b/packages/pieces/community/google-contacts/src/lib/trigger/new-contact.ts @@ -10,6 +10,7 @@ import { pollingHelper, } from '@activepieces/pieces-common'; import { googleContactsAuth } from '../auth'; +import { newOrUpdatedContactOutputSchema } from '../output-schemas'; import { people as googlePeople } from '@googleapis/people'; import { OAuth2Client } from 'google-auth-library'; import dayjs from 'dayjs'; @@ -91,6 +92,7 @@ export const googleContactNewOrUpdatedContact = createTrigger({ description: 'Fires when a contact in the authenticated Google Contacts account is newly created or has been modified since the last poll. Each event represents one person record with its current fields; deleted contacts are excluded.', }, props: {}, + outputSchema: newOrUpdatedContactOutputSchema, sampleData: { resourceName: 'people/c4278485694217203807', etag: '%EiMBAgMFBgcICQoLDA0ODxATFBUWGSEiIyQlJicuNDU3PT4/QBoEAQIFByIMZFVwNlJPNEVKUzg9', diff --git a/packages/pieces/community/imap/package.json b/packages/pieces/community/imap/package.json index 06b54ebac8d9..21cfa9fc8557 100644 --- a/packages/pieces/community/imap/package.json +++ b/packages/pieces/community/imap/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-imap", - "version": "0.4.7", + "version": "0.4.8", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "dependencies": { diff --git a/packages/pieces/community/imap/src/lib/actions/copy-email.ts b/packages/pieces/community/imap/src/lib/actions/copy-email.ts index 5a628aab2a8d..82a1511ad074 100644 --- a/packages/pieces/community/imap/src/lib/actions/copy-email.ts +++ b/packages/pieces/community/imap/src/lib/actions/copy-email.ts @@ -4,6 +4,7 @@ import { mailboxDropdown, copyEmail as copyImapEmail, } from '../common'; +import { copyEmailActionOutputSchema } from '../output-schemas'; const props = { sourceMailbox: mailboxDropdown({ @@ -28,6 +29,7 @@ export const copyEmail = createAction({ name: 'copy_email', displayName: 'Copy Email', description: 'Copy an email to another mailbox', + outputSchema: copyEmailActionOutputSchema, audience: 'both', aiMetadata: { description: 'Copies an email (by message UID) from a source IMAP folder into a target folder, leaving the original in place. Use to duplicate a message into another mailbox without removing it from the source. Requires the source folder, target folder, and the UID; not idempotent since each call appends another copy with a new UID.', idempotent: false }, props, diff --git a/packages/pieces/community/imap/src/lib/actions/move-email.ts b/packages/pieces/community/imap/src/lib/actions/move-email.ts index 9e2670a804a0..3f05778439bc 100644 --- a/packages/pieces/community/imap/src/lib/actions/move-email.ts +++ b/packages/pieces/community/imap/src/lib/actions/move-email.ts @@ -1,5 +1,6 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { imapAuth, mailboxDropdown, moveEmail as moveImapEmail } from '../common'; +import { moveEmailActionOutputSchema } from '../output-schemas'; const props = { sourceMailbox: mailboxDropdown({ @@ -24,6 +25,7 @@ export const moveEmail = createAction({ name: 'move_email', displayName: 'Move Email', description: 'Move an email to another mailbox', + outputSchema: moveEmailActionOutputSchema, audience: 'both', aiMetadata: { description: 'Moves an email (by message UID) from a source IMAP folder to a target folder, removing it from the source. Use to file, archive, or send a message to a Trash folder. Requires the source folder, target folder, and the UID; not idempotent since after the move the message no longer exists at the source UID and a repeat call cannot find it.', idempotent: false }, props, diff --git a/packages/pieces/community/imap/src/lib/output-schemas.ts b/packages/pieces/community/imap/src/lib/output-schemas.ts new file mode 100644 index 000000000000..6e1e2802a54a --- /dev/null +++ b/packages/pieces/community/imap/src/lib/output-schemas.ts @@ -0,0 +1,69 @@ +import { OutputSchema } from '@activepieces/pieces-framework'; + +const addressFields: OutputSchema['fields'] = [ + { key: 'text', label: 'Address Text' }, + { + key: 'value', + label: 'Addresses', + labelKey: 'address', + listItems: [ + { key: 'address', label: 'Email', format: 'email' }, + { key: 'name', label: 'Name' }, + ], + }, +]; + +export const newEmailTriggerOutputSchema: OutputSchema = { + fields: [ + { key: 'subject', label: 'Subject' }, + { key: 'date', label: 'Date', format: 'datetime' }, + { key: 'from', label: 'From', children: addressFields }, + { key: 'to', label: 'To', children: addressFields }, + { key: 'replyTo', label: 'Reply To', children: addressFields }, + { key: 'text', label: 'Text Body' }, + { key: 'html', label: 'HTML Body', format: 'html' }, + { key: 'textAsHtml', label: 'Text Body as HTML', format: 'html' }, + { key: 'messageId', label: 'Message ID' }, + { key: 'uid', label: 'Message UID', format: 'number' }, + { + key: 'attachments', + label: 'Attachments', + description: + 'Stored attachment references, one per attachment — each a signed download URL produced by writing the attachment to file storage. Pass an entry straight to an action that accepts a file or a URL; the original filename and content type are not carried here.', + }, + { + key: 'headerLines', + label: 'Header Lines', + labelKey: 'key', + listItems: [ + { key: 'key', label: 'Header Name' }, + { key: 'line', label: 'Raw Header Line' }, + ], + }, + ], +}; + +export const copyEmailActionOutputSchema: OutputSchema = { + fields: [ + { key: 'success', label: 'Success', format: 'boolean' }, + { + key: 'newUid', + label: 'New Message UID', + format: 'number', + description: 'UID of the copy in the target folder.', + }, + ], +}; + +export const moveEmailActionOutputSchema: OutputSchema = { + fields: [ + { key: 'success', label: 'Success', format: 'boolean' }, + { + key: 'newUid', + label: 'New Message UID', + format: 'number', + description: + 'UID of the message in the target folder. Absent when the server reports no new UID, in which case Success is false.', + }, + ], +}; diff --git a/packages/pieces/community/imap/src/lib/triggers/new-email.ts b/packages/pieces/community/imap/src/lib/triggers/new-email.ts index e0beacfb582f..a2ad666c7ad8 100644 --- a/packages/pieces/community/imap/src/lib/triggers/new-email.ts +++ b/packages/pieces/community/imap/src/lib/triggers/new-email.ts @@ -20,6 +20,7 @@ import { mailboxDropdown, fetchEmails, } from '../common'; +import { newEmailTriggerOutputSchema } from '../output-schemas'; const filterInstructions = ` **Emails Filtering:** @@ -73,6 +74,7 @@ export const newEmail = createTrigger({ name: 'new_email', displayName: 'New Email', description: 'Trigger when a new email is received', + outputSchema: newEmailTriggerOutputSchema, aiMetadata: { description: 'Fires when a new email arrives in the selected IMAP mailbox folder. Polls the folder on an interval and emits one event per newly received message, including its parsed content and any attachments. Represents an inbound email landing in that mailbox.', }, diff --git a/packages/pieces/community/resend/package.json b/packages/pieces/community/resend/package.json index 7abc63d5003d..161632d1cc0d 100644 --- a/packages/pieces/community/resend/package.json +++ b/packages/pieces/community/resend/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-resend", - "version": "0.4.4", + "version": "0.4.5", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "scripts": { diff --git a/packages/pieces/community/resend/src/lib/actions/cancel-scheduled-email.action.ts b/packages/pieces/community/resend/src/lib/actions/cancel-scheduled-email.action.ts index 172c6359d0d9..10b69f440063 100644 --- a/packages/pieces/community/resend/src/lib/actions/cancel-scheduled-email.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/cancel-scheduled-email.action.ts @@ -1,11 +1,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { cancelScheduledEmailOutputSchema } from '../output-schemas'; export const cancelScheduledEmail = createAction({ name: 'cancel_scheduled_email', auth: resendAuth, displayName: 'Cancel Scheduled Email', + outputSchema: cancelScheduledEmailOutputSchema, description: 'Cancel a scheduled email before it is sent', audience: 'both', aiMetadata: { description: 'Cancels a previously scheduled email so it will not be delivered, identified by its Resend email ID. Use this to stop a future send before its scheduled time. Effectively idempotent — once an email is cancelled, repeating the call has no further effect; only works while the email is still pending.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/create-audience.action.ts b/packages/pieces/community/resend/src/lib/actions/create-audience.action.ts index 5e6ff4705550..af4c73bcfbe3 100644 --- a/packages/pieces/community/resend/src/lib/actions/create-audience.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/create-audience.action.ts @@ -1,11 +1,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { createAudienceOutputSchema } from '../output-schemas'; export const createAudience = createAction({ name: 'create_audience', auth: resendAuth, displayName: 'Create Audience', + outputSchema: createAudienceOutputSchema, description: 'Create a new contact audience in Resend', audience: 'both', aiMetadata: { description: 'Creates a new contact audience (mailing list) in Resend with the given name and returns its ID. Use this before adding contacts or sending broadcasts when no suitable audience exists yet. Not idempotent — each call creates a new audience even if the name matches an existing one.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/create-broadcast.action.ts b/packages/pieces/community/resend/src/lib/actions/create-broadcast.action.ts index 94b3a0c556dc..eac20639504d 100644 --- a/packages/pieces/community/resend/src/lib/actions/create-broadcast.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/create-broadcast.action.ts @@ -1,4 +1,5 @@ import { createAction, Property } from '@activepieces/pieces-framework'; +import { createBroadcastOutputSchema } from '../output-schemas'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; @@ -7,6 +8,7 @@ export const createBroadcast = createAction({ name: 'create_broadcast', auth: resendAuth, displayName: 'Create Broadcast', + outputSchema: createBroadcastOutputSchema, description: 'Create a new broadcast email to send to an audience', audience: 'both', aiMetadata: { description: 'Creates a draft broadcast email (subject and body) targeting a Resend audience and returns its ID. Use this to prepare a campaign; it only creates the draft and does not deliver anything — pair it with Send Broadcast to actually send. Requires the audience ID and a sender on a verified domain. Not idempotent — each call creates a new broadcast.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/create-contact.action.ts b/packages/pieces/community/resend/src/lib/actions/create-contact.action.ts index 203ba84a1894..5a5bf97d860b 100644 --- a/packages/pieces/community/resend/src/lib/actions/create-contact.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/create-contact.action.ts @@ -2,11 +2,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { createContactOutputSchema } from '../output-schemas'; export const createContact = createAction({ name: 'create_contact', auth: resendAuth, displayName: 'Create Contact', + outputSchema: createContactOutputSchema, description: 'Add a contact to a Resend audience', audience: 'both', aiMetadata: { description: 'Adds a new contact (email plus optional name and subscription status) to a specific Resend audience. Use this to grow a mailing list; requires the target audience ID (use List Audiences to find it). Not idempotent — each call creates a new contact, so guard against re-adding the same email.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/create-domain.action.ts b/packages/pieces/community/resend/src/lib/actions/create-domain.action.ts index 1313a65cdc63..06654fb59534 100644 --- a/packages/pieces/community/resend/src/lib/actions/create-domain.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/create-domain.action.ts @@ -1,11 +1,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { createDomainOutputSchema } from '../output-schemas'; export const createDomain = createAction({ name: 'create_domain', auth: resendAuth, displayName: 'Create Domain', + outputSchema: createDomainOutputSchema, description: 'Add a sending domain to your Resend account and get the DNS records to verify it', audience: 'both', aiMetadata: { description: 'Registers a new sending domain on the Resend account and returns the DNS records that must be added to verify it. Use this as the first step in setting up a custom sending domain before triggering verification. Not idempotent — each call attempts to create a new domain entry.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/delete-audience.action.ts b/packages/pieces/community/resend/src/lib/actions/delete-audience.action.ts index b136e5fa2176..5c4b23624dad 100644 --- a/packages/pieces/community/resend/src/lib/actions/delete-audience.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/delete-audience.action.ts @@ -2,11 +2,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { deleteAudienceOutputSchema } from '../output-schemas'; export const deleteAudience = createAction({ name: 'delete_audience', auth: resendAuth, displayName: 'Delete Audience', + outputSchema: deleteAudienceOutputSchema, description: 'Permanently delete an audience and all its contacts', audience: 'both', aiMetadata: { description: 'Permanently deletes an entire audience and all of its contacts from Resend, identified by audience ID. Use this to remove a whole mailing list; this is destructive and cannot be undone. Effectively idempotent — once deleted, repeating the call has no further effect.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/delete-broadcast.action.ts b/packages/pieces/community/resend/src/lib/actions/delete-broadcast.action.ts index 245bebb206c2..7799853d8808 100644 --- a/packages/pieces/community/resend/src/lib/actions/delete-broadcast.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/delete-broadcast.action.ts @@ -1,4 +1,5 @@ import { createAction } from '@activepieces/pieces-framework'; +import { deleteBroadcastOutputSchema } from '../output-schemas'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; @@ -7,6 +8,7 @@ export const deleteBroadcast = createAction({ name: 'delete_broadcast', auth: resendAuth, displayName: 'Delete Broadcast', + outputSchema: deleteBroadcastOutputSchema, description: 'Permanently delete a broadcast from your Resend account', audience: 'both', aiMetadata: { description: 'Permanently deletes a broadcast from the Resend account, identified by broadcast ID. Use this to remove a draft or unwanted campaign; only broadcasts that have not been sent can be deleted. Effectively idempotent — once deleted, repeating the call has no further effect.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/delete-contact.action.ts b/packages/pieces/community/resend/src/lib/actions/delete-contact.action.ts index b2449a84aedf..242c034f1c9e 100644 --- a/packages/pieces/community/resend/src/lib/actions/delete-contact.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/delete-contact.action.ts @@ -2,11 +2,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { deleteContactOutputSchema } from '../output-schemas'; export const deleteContact = createAction({ name: 'delete_contact', auth: resendAuth, displayName: 'Delete Contact', + outputSchema: deleteContactOutputSchema, description: 'Remove a contact from an audience', audience: 'both', aiMetadata: { description: 'Permanently removes a contact from a Resend audience, identified by audience ID and contact ID. Use this to delete a subscriber from a list. Effectively idempotent — once removed, repeating the call has no further effect.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/delete-domain.action.ts b/packages/pieces/community/resend/src/lib/actions/delete-domain.action.ts index 73eb8c4911ad..97d6c551680c 100644 --- a/packages/pieces/community/resend/src/lib/actions/delete-domain.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/delete-domain.action.ts @@ -2,11 +2,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { deleteDomainOutputSchema } from '../output-schemas'; export const deleteDomain = createAction({ name: 'delete_domain', auth: resendAuth, displayName: 'Delete Domain', + outputSchema: deleteDomainOutputSchema, description: 'Remove a domain from your Resend account', audience: 'both', aiMetadata: { description: 'Permanently removes a sending domain from the Resend account, identified by its domain ID. Use this to decommission a domain you no longer send from. Effectively idempotent — once deleted, repeating the call has no further effect.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/get-email-status.action.ts b/packages/pieces/community/resend/src/lib/actions/get-email-status.action.ts index c98fa2482f10..c228d460f845 100644 --- a/packages/pieces/community/resend/src/lib/actions/get-email-status.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/get-email-status.action.ts @@ -5,11 +5,13 @@ import { httpClient, } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { getEmailStatusOutputSchema } from '../output-schemas'; export const getEmailStatus = createAction({ name: 'get_email_status', auth: resendAuth, displayName: 'Get Email Status', + outputSchema: getEmailStatusOutputSchema, description: 'Retrieve the delivery status of a sent email', audience: 'both', aiMetadata: { description: 'Looks up the current delivery status and details of a single previously sent email by its Resend email ID. Use this to check whether a specific email was delivered, bounced, or is still scheduled. Read-only and idempotent; requires the email ID returned when the email was sent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/list-audiences.action.ts b/packages/pieces/community/resend/src/lib/actions/list-audiences.action.ts index 9f3f25735c1f..eeca30c78501 100644 --- a/packages/pieces/community/resend/src/lib/actions/list-audiences.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/list-audiences.action.ts @@ -1,11 +1,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { listAudiencesOutputSchema } from '../output-schemas'; export const listAudiences = createAction({ name: 'list_audiences', auth: resendAuth, displayName: 'List Audiences', + outputSchema: listAudiencesOutputSchema, description: 'Retrieve all contact audiences in your Resend account', audience: 'both', aiMetadata: { description: 'Retrieves all contact audiences (mailing lists) in the connected Resend account, including each audience\'s ID and name. Use this to discover an audience ID needed by contact and broadcast actions (e.g. Create Contact, List Contacts, Create Broadcast). Read-only and idempotent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/list-broadcasts.action.ts b/packages/pieces/community/resend/src/lib/actions/list-broadcasts.action.ts index 2ab1d24d23ab..6e48b0ae4243 100644 --- a/packages/pieces/community/resend/src/lib/actions/list-broadcasts.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/list-broadcasts.action.ts @@ -1,4 +1,5 @@ import { createAction } from '@activepieces/pieces-framework'; +import { listBroadcastsOutputSchema } from '../output-schemas'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; @@ -20,6 +21,7 @@ export const listBroadcasts = createAction({ name: 'list_broadcasts', auth: resendAuth, displayName: 'List Broadcasts', + outputSchema: listBroadcastsOutputSchema, description: 'Retrieve all broadcasts in your Resend account', audience: 'both', aiMetadata: { description: 'Retrieves all broadcast campaigns in the connected Resend account, including each broadcast\'s ID, name, audience, subject, and status (draft, scheduled, sent). Use this to find a broadcast ID (e.g. for Send Broadcast or Delete Broadcast) or to check campaign status. Read-only and idempotent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/list-contacts.action.ts b/packages/pieces/community/resend/src/lib/actions/list-contacts.action.ts index b80cc3cf49d9..945d9b231ef8 100644 --- a/packages/pieces/community/resend/src/lib/actions/list-contacts.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/list-contacts.action.ts @@ -2,11 +2,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { listContactsOutputSchema } from '../output-schemas'; export const listContacts = createAction({ name: 'list_contacts', auth: resendAuth, displayName: 'List Contacts', + outputSchema: listContactsOutputSchema, description: 'Retrieve all contacts in an audience', audience: 'both', aiMetadata: { description: 'Retrieves all contacts in a specific Resend audience, including each contact\'s ID, email, name, and subscription status. Use this to discover contact IDs (e.g. to feed Update Contact or Delete Contact) or to inspect a mailing list; requires the audience ID. Read-only and idempotent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/list-domains.action.ts b/packages/pieces/community/resend/src/lib/actions/list-domains.action.ts index 4c7efa138589..73cd4b9b0afb 100644 --- a/packages/pieces/community/resend/src/lib/actions/list-domains.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/list-domains.action.ts @@ -1,6 +1,7 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { listDomainsOutputSchema } from '../output-schemas'; interface DomainRecord { id: string; @@ -15,6 +16,7 @@ export const listDomains = createAction({ name: 'list_domains', auth: resendAuth, displayName: 'List Domains', + outputSchema: listDomainsOutputSchema, description: 'Retrieve all domains added to your Resend account', audience: 'both', aiMetadata: { description: 'Retrieves all sending domains configured on the connected Resend account, including each domain\'s ID, name, verification status, and region. Use this to find a domain ID (e.g. for Verify Domain or Delete Domain) or to check which domains are verified before sending. Read-only and idempotent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/list-emails.action.ts b/packages/pieces/community/resend/src/lib/actions/list-emails.action.ts index 91546e9e7fc4..72fb46c4836d 100644 --- a/packages/pieces/community/resend/src/lib/actions/list-emails.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/list-emails.action.ts @@ -1,6 +1,7 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { listEmailsOutputSchema } from '../output-schemas'; interface EmailRecord { id: string; @@ -19,6 +20,7 @@ export const listEmails = createAction({ name: 'list_emails', auth: resendAuth, displayName: 'List Sent Emails', + outputSchema: listEmailsOutputSchema, description: 'Retrieve a list of emails sent from your Resend account', audience: 'both', aiMetadata: { description: 'Retrieves the list of emails sent from the connected Resend account, including their IDs, recipients, subjects, and latest delivery event. Use this to discover email IDs (e.g. to feed Get Email Status, Cancel Scheduled Email, or Reschedule Email) or to audit recent sends. Read-only and idempotent.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/reschedule-email.action.ts b/packages/pieces/community/resend/src/lib/actions/reschedule-email.action.ts index c276ed33fb7b..7c17cacf8276 100644 --- a/packages/pieces/community/resend/src/lib/actions/reschedule-email.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/reschedule-email.action.ts @@ -1,11 +1,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { rescheduleEmailOutputSchema } from '../output-schemas'; export const rescheduleEmail = createAction({ name: 'reschedule_email', auth: resendAuth, displayName: 'Reschedule Email', + outputSchema: rescheduleEmailOutputSchema, description: 'Update the send time of a scheduled email', audience: 'both', aiMetadata: { description: 'Changes the future send time of an already scheduled email, identified by its Resend email ID. Use this to move a pending send to a new time without cancelling and recreating it. Idempotent when called with the same target time; requires an ISO 8601 date-time and only works while the email is still scheduled.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/send-batch-emails.action.ts b/packages/pieces/community/resend/src/lib/actions/send-batch-emails.action.ts index 1efe6f8d8564..4918e897af08 100644 --- a/packages/pieces/community/resend/src/lib/actions/send-batch-emails.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/send-batch-emails.action.ts @@ -5,6 +5,7 @@ import { httpClient, } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { sendBatchEmailsOutputSchema } from '../output-schemas'; const BASE_URL = 'https://api.resend.com'; @@ -12,6 +13,7 @@ export const sendBatchEmails = createAction({ name: 'send_batch_emails', auth: resendAuth, displayName: 'Send Batch Emails', + outputSchema: sendBatchEmailsOutputSchema, description: 'Send up to 100 emails in a single API call', audience: 'both', aiMetadata: { description: 'Sends up to 100 distinct emails in one Resend API call, each with its own recipient, subject, and body. Use this instead of Send Email when delivering many independent messages at once. Senders must be on verified domains; pass an idempotency key to guard against duplicate batch sends, otherwise each call sends new emails.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/send-broadcast.action.ts b/packages/pieces/community/resend/src/lib/actions/send-broadcast.action.ts index 7f56146f8d98..81e0d4b5ac2d 100644 --- a/packages/pieces/community/resend/src/lib/actions/send-broadcast.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/send-broadcast.action.ts @@ -1,4 +1,5 @@ import { createAction, Property } from '@activepieces/pieces-framework'; +import { sendBroadcastOutputSchema } from '../output-schemas'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; @@ -7,6 +8,7 @@ export const sendBroadcast = createAction({ name: 'send_broadcast', auth: resendAuth, displayName: 'Send Broadcast', + outputSchema: sendBroadcastOutputSchema, description: 'Send or schedule a broadcast email to its audience', audience: 'both', aiMetadata: { description: 'Sends an existing draft broadcast to its entire audience immediately, or schedules it for a future time, identified by broadcast ID. Use this after Create Broadcast to deliver the campaign. Not idempotent — repeating the call can dispatch the broadcast again; optionally pass an ISO 8601 time to schedule rather than send now.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/send-email.ts b/packages/pieces/community/resend/src/lib/actions/send-email.ts index ecd5d39aaa8b..4e8bc715ba07 100644 --- a/packages/pieces/community/resend/src/lib/actions/send-email.ts +++ b/packages/pieces/community/resend/src/lib/actions/send-email.ts @@ -5,11 +5,13 @@ import { httpClient, } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; +import { sendEmailOutputSchema } from '../output-schemas'; export const sendEmail = createAction({ auth: resendAuth, name: 'send_email', displayName: 'Send Email', + outputSchema: sendEmailOutputSchema, description: 'Send a text or HTML email', audience: 'both', aiMetadata: { description: 'Sends a single transactional email (plain text or HTML) to one or more recipients via Resend. Use this for one-off messages; the sender address must be on a domain you have verified in Resend. Not idempotent — each call sends a new email.', idempotent: false }, diff --git a/packages/pieces/community/resend/src/lib/actions/update-contact.action.ts b/packages/pieces/community/resend/src/lib/actions/update-contact.action.ts index 17619372e00c..3af8f0d856f3 100644 --- a/packages/pieces/community/resend/src/lib/actions/update-contact.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/update-contact.action.ts @@ -2,11 +2,13 @@ import { createAction, Property } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { updateContactOutputSchema } from '../output-schemas'; export const updateContact = createAction({ name: 'update_contact', auth: resendAuth, displayName: 'Update Contact', + outputSchema: updateContactOutputSchema, description: 'Update the name or subscription status of a contact in an audience', audience: 'both', aiMetadata: { description: 'Updates an existing contact\'s first name, last name, or subscribed/unsubscribed status within an audience, identified by audience ID and contact ID. Use this to change details or toggle subscription for a known contact. Idempotent — repeating with the same values leaves the contact in the same state.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/actions/verify-domain.action.ts b/packages/pieces/community/resend/src/lib/actions/verify-domain.action.ts index 548cbc7d8154..e18b7ba76cf5 100644 --- a/packages/pieces/community/resend/src/lib/actions/verify-domain.action.ts +++ b/packages/pieces/community/resend/src/lib/actions/verify-domain.action.ts @@ -2,11 +2,13 @@ import { createAction } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common'; import { resendAuth } from '../..'; import { resendProps } from '../common/props'; +import { verifyDomainOutputSchema } from '../output-schemas'; export const verifyDomain = createAction({ name: 'verify_domain', auth: resendAuth, displayName: 'Verify Domain', + outputSchema: verifyDomainOutputSchema, description: 'Trigger a DNS verification check for a domain', audience: 'both', aiMetadata: { description: 'Triggers Resend to re-check the DNS records of a domain and update its verification status, identified by domain ID. Use this after adding the required DNS records (from Create Domain) to confirm the domain is ready to send. Safe to call repeatedly — it only re-runs the check.', idempotent: true }, diff --git a/packages/pieces/community/resend/src/lib/output-schemas.ts b/packages/pieces/community/resend/src/lib/output-schemas.ts new file mode 100644 index 000000000000..8b3ae7628204 --- /dev/null +++ b/packages/pieces/community/resend/src/lib/output-schemas.ts @@ -0,0 +1,212 @@ +import { OutputSchema } from '@activepieces/pieces-framework'; + +// Most actions return response.body, so their paths are bare. send_email is the one +// exception: it returns the whole HttpResponse, so every path there is body.*. + +const contactFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Contact ID' }, + { key: 'email', label: 'Email', format: 'email' }, + { key: 'first_name', label: 'First Name' }, + { key: 'last_name', label: 'Last Name' }, + { key: 'unsubscribed', label: 'Unsubscribed', format: 'boolean' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, +]; + +const domainFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Domain ID' }, + { key: 'name', label: 'Domain Name' }, + { key: 'status', label: 'Status' }, + { key: 'region', label: 'Region' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, + { key: 'sending', label: 'Sending Capability' }, + { key: 'receiving', label: 'Receiving Capability' }, +]; + +const audienceFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Audience ID' }, + { key: 'name', label: 'Name' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, +]; + +const emailRowFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Email ID' }, + { key: 'from', label: 'From' }, + { key: 'to', label: 'To', description: 'Recipients, comma-separated.' }, + { key: 'subject', label: 'Subject' }, + { key: 'last_event', label: 'Last Event', description: 'Latest delivery state, e.g. delivered, bounced, scheduled.' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, + { key: 'scheduled_at', label: 'Scheduled At', description: 'Send time for a scheduled email, empty otherwise.' }, + { key: 'cc', label: 'CC', description: 'CC recipients, comma-separated.' }, + { key: 'bcc', label: 'BCC', description: 'BCC recipients, comma-separated.' }, + { key: 'reply_to', label: 'Reply To', description: 'Reply-to addresses, comma-separated.' }, +]; + +// send_email returns the full HttpResponse. Only the id is worth surfacing — status and +// headers are transport, and the headers carry x-resend-*-quota. +export const sendEmailOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Email ID', value: 'body.id' }], +}; + +export const sendBatchEmailsOutputSchema: OutputSchema = { + itemLabel: 'Email {id}', + fields: [ + { + key: 'emails', + label: 'Sent Emails', + value: '', + labelKey: 'id', + listItems: [{ key: 'id', label: 'Email ID' }], + }, + ], +}; + +export const getEmailStatusOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Email ID' }, + { key: 'subject', label: 'Subject' }, + { key: 'from', label: 'From' }, + { key: 'to', label: 'To', description: 'Recipient addresses, as a list of strings.' }, + { key: 'cc', label: 'CC', description: 'CC addresses as a list of strings, or empty when none were set.' }, + { key: 'bcc', label: 'BCC', description: 'BCC addresses as a list of strings, or empty when none were set.' }, + { key: 'reply_to', label: 'Reply To', description: 'Reply-to addresses, as a list of strings.' }, + { key: 'last_event', label: 'Last Event', description: 'Latest delivery state, e.g. delivered, bounced, scheduled.' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, + { key: 'scheduled_at', label: 'Scheduled At', format: 'datetime', description: 'Send time for a scheduled email, null otherwise.' }, + { key: 'message_id', label: 'Message ID', description: 'RFC 5322 Message-ID assigned by the sending infrastructure.' }, + { key: 'text', label: 'Text Body' }, + { key: 'html', label: 'HTML Body', format: 'html' }, + ], +}; + +export const listEmailsOutputSchema: OutputSchema = { + itemLabel: '{subject}', + fields: [ + { key: 'emails', label: 'Emails', value: '', labelKey: 'subject', listItems: emailRowFields }, + ], +}; + +export const cancelScheduledEmailOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Email ID' }], +}; + +export const rescheduleEmailOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Email ID' }], +}; + +export const createAudienceOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Audience ID' }, + { key: 'name', label: 'Name' }, + ], +}; + +export const listAudiencesOutputSchema: OutputSchema = { + itemLabel: '{name}', + fields: [ + { key: 'audiences', label: 'Audiences', value: '', labelKey: 'name', listItems: audienceFields }, + ], +}; + +export const deleteAudienceOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Audience ID' }, + { key: 'deleted', label: 'Deleted', format: 'boolean' }, + ], +}; + +export const createContactOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Contact ID' }], +}; + +export const updateContactOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Contact ID' }], +}; + +// delete_contact answers with `contact`, not `id`, unlike the other deletes. +export const deleteContactOutputSchema: OutputSchema = { + fields: [ + { key: 'contact', label: 'Contact ID' }, + { key: 'deleted', label: 'Deleted', format: 'boolean' }, + ], +}; + +export const listContactsOutputSchema: OutputSchema = { + itemLabel: '{email}', + fields: [ + { key: 'contacts', label: 'Contacts', value: '', labelKey: 'email', listItems: contactFields }, + ], +}; + +export const createDomainOutputSchema: OutputSchema = { + fields: [ + ...domainFields, + { + key: 'dns_records', + label: 'DNS Records', + labelKey: 'name', + description: 'Records to add at your DNS provider before the domain will verify.', + listItems: [ + { key: 'record', label: 'Record Purpose', description: 'DKIM or SPF.' }, + { key: 'name', label: 'Host' }, + { key: 'type', label: 'DNS Type', description: 'TXT or MX.' }, + { key: 'value', label: 'Value' }, + { key: 'ttl', label: 'TTL' }, + { key: 'status', label: 'Status' }, + { key: 'priority', label: 'Priority', format: 'number', description: 'Set on MX records only.' }, + ], + }, + ], +}; + +export const verifyDomainOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Domain ID' }], +}; + +export const deleteDomainOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Domain ID' }, + { key: 'deleted', label: 'Deleted', format: 'boolean' }, + ], +}; + +export const listDomainsOutputSchema: OutputSchema = { + itemLabel: '{name}', + fields: [ + { key: 'domains', label: 'Domains', value: '', labelKey: 'name', listItems: domainFields }, + ], +}; + +const broadcastFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Broadcast ID' }, + { key: 'name', label: 'Name' }, + { key: 'audience_id', label: 'Audience ID' }, + { key: 'status', label: 'Status' }, + { key: 'reply_to', label: 'Reply To' }, + { key: 'preview_text', label: 'Preview Text' }, + { key: 'created_at', label: 'Created At', format: 'datetime' }, + // Empty strings until the broadcast is scheduled or sent, rather than absent keys. + { key: 'scheduled_at', label: 'Scheduled At', format: 'datetime' }, + { key: 'sent_at', label: 'Sent At', format: 'datetime' }, +]; + +export const createBroadcastOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Broadcast ID' }], +}; + +export const sendBroadcastOutputSchema: OutputSchema = { + fields: [{ key: 'id', label: 'Broadcast ID' }], +}; + +export const deleteBroadcastOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Broadcast ID' }, + { key: 'deleted', label: 'Deleted', format: 'boolean' }, + ], +}; + +export const listBroadcastsOutputSchema: OutputSchema = { + itemLabel: '{name}', + fields: [ + { key: 'broadcasts', label: 'Broadcasts', value: '', labelKey: 'name', listItems: broadcastFields }, + ], +}; diff --git a/packages/pieces/community/wordpress/package.json b/packages/pieces/community/wordpress/package.json index 35cb6fa6ebdc..6dd8209a70a0 100644 --- a/packages/pieces/community/wordpress/package.json +++ b/packages/pieces/community/wordpress/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-wordpress", - "version": "0.4.7", + "version": "0.4.8", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "scripts": { diff --git a/packages/pieces/community/wordpress/src/lib/actions/create-page.action.ts b/packages/pieces/community/wordpress/src/lib/actions/create-page.action.ts index bf74f674e1f0..8cbd164a0407 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/create-page.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/create-page.action.ts @@ -7,6 +7,7 @@ import { AuthenticationType, } from '@activepieces/pieces-common'; import { wordpressAuth } from '../..'; +import { createPageActionOutputSchema } from '../output-schemas'; export const createWordPressPage = createAction({ auth: wordpressAuth, @@ -15,6 +16,7 @@ export const createWordPressPage = createAction({ audience: 'both', aiMetadata: { description: 'Publishes a new static page (not a blog post) on a WordPress site via the REST API, with optional status, slug, excerpt, and comment settings. Choose this for standalone pages like About or Contact rather than dated posts. Requires a title and HTML content; not idempotent — each call creates a separate page.', idempotent: false }, displayName: 'Create Page', + outputSchema: createPageActionOutputSchema, props: { title: Property.ShortText({ description: 'Title of the page about to be added', diff --git a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts index e4f22a9d91d8..316ab6909893 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/create-post.action.ts @@ -11,6 +11,7 @@ import { } from '@activepieces/pieces-common'; import FormData from 'form-data'; import { wordpressAuth } from '../..'; +import { createPostActionOutputSchema } from '../output-schemas'; export const createWordPressPost = createAction({ auth: wordpressAuth, @@ -19,6 +20,7 @@ export const createWordPressPost = createAction({ audience: 'both', aiMetadata: { description: 'Publishes a new blog post on a WordPress site via the REST API, with optional status (draft/publish/etc.), categories, tags, excerpt, featured image, and custom ACF fields. Choose this to add fresh content; for editing an existing post use Update Post. Requires a title and HTML content; not idempotent — each call creates a separate post.', idempotent: false }, displayName: 'Create Post', + outputSchema: createPostActionOutputSchema, props: { title: Property.ShortText({ description: 'Title of the post about to be added', diff --git a/packages/pieces/community/wordpress/src/lib/actions/get-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/get-post.action.ts index 4a9073f37a01..35c01066a07f 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/get-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/get-post.action.ts @@ -11,6 +11,7 @@ import { } from '@activepieces/pieces-common'; import FormData from 'form-data'; import { wordpressAuth } from '../..'; +import { getPostActionOutputSchema } from '../output-schemas'; export const getWordPressPost = createAction({ auth: wordpressAuth, @@ -19,6 +20,7 @@ export const getWordPressPost = createAction({ audience: 'both', aiMetadata: { description: 'Fetches a single WordPress post by its numeric post ID via the REST API. Use it to read the current content, status, or metadata of a known post before acting on it. Requires the post ID; read-only and idempotent.', idempotent: true }, displayName: 'Get Post Details', + outputSchema: getPostActionOutputSchema, props: { id: Property.Number({ description: 'The ID of the post to get', diff --git a/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts b/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts index 70a6313902a5..ff24b5041dd5 100644 --- a/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts +++ b/packages/pieces/community/wordpress/src/lib/actions/update-post.action.ts @@ -7,6 +7,7 @@ import { } from '@activepieces/pieces-common'; import FormData from 'form-data'; import { wordpressAuth } from '../..'; +import { updatePostActionOutputSchema } from '../output-schemas'; export const updateWordPressPost = createAction({ auth: wordpressAuth, @@ -15,6 +16,7 @@ export const updateWordPressPost = createAction({ audience: 'both', aiMetadata: { description: 'Updates an existing WordPress post identified by its post ID, changing only the fields you supply (title, content, status, categories, tags, excerpt, featured image, or ACF fields). Choose this to edit or republish a known post rather than create a new one. Requires the target post ID; idempotent — repeating with the same input leaves the post in the same final state.', idempotent: true }, displayName: 'Update Post', + outputSchema: updatePostActionOutputSchema, props: { post: wordpressCommon.post, title: Property.ShortText({ diff --git a/packages/pieces/community/wordpress/src/lib/output-schemas.ts b/packages/pieces/community/wordpress/src/lib/output-schemas.ts new file mode 100644 index 000000000000..70899cae39e4 --- /dev/null +++ b/packages/pieces/community/wordpress/src/lib/output-schemas.ts @@ -0,0 +1,148 @@ +import { OutputSchema } from '@activepieces/pieces-framework'; + +const renderedTextFields: OutputSchema['fields'] = [ + { key: 'rendered', label: 'Rendered', format: 'html' }, +]; + +const renderedUrlFields: OutputSchema['fields'] = [ + { key: 'rendered', label: 'Rendered', format: 'url' }, +]; + +const renderedBodyFields: OutputSchema['fields'] = [ + { key: 'rendered', label: 'Rendered', format: 'html' }, + { key: 'protected', label: 'Password Protected', format: 'boolean' }, +]; + +export const createPostActionOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Post ID', value: 'body.id', format: 'number' }, + { key: 'title', label: 'Title', value: 'body.title', children: renderedTextFields }, + { key: 'link', label: 'Link', value: 'body.link', format: 'url' }, + { key: 'slug', label: 'Slug', value: 'body.slug' }, + { key: 'status', label: 'Status', value: 'body.status' }, + { key: 'type', label: 'Type', value: 'body.type' }, + { key: 'date', label: 'Published Date', value: 'body.date', format: 'datetime' }, + { key: 'date_gmt', label: 'Published Date (GMT)', value: 'body.date_gmt', format: 'datetime' }, + { key: 'modified', label: 'Modified Date', value: 'body.modified', format: 'datetime' }, + { key: 'modified_gmt', label: 'Modified Date (GMT)', value: 'body.modified_gmt', format: 'datetime' }, + { key: 'content', label: 'Content', value: 'body.content', children: renderedBodyFields }, + { key: 'excerpt', label: 'Excerpt', value: 'body.excerpt', children: renderedBodyFields }, + { key: 'guid', label: 'GUID', value: 'body.guid', children: renderedUrlFields }, + { key: 'author', label: 'Author ID', value: 'body.author', format: 'number' }, + { key: 'featured_media', label: 'Featured Media ID', value: 'body.featured_media', format: 'number' }, + { key: 'sticky', label: 'Sticky', value: 'body.sticky', format: 'boolean' }, + { key: 'format', label: 'Format', value: 'body.format' }, + { + key: 'categories', + label: 'Category IDs', + value: 'body.categories', + description: 'Category term IDs assigned to the post, as a list of numbers.', + }, + { + key: 'tags', + label: 'Tag IDs', + value: 'body.tags', + description: 'Tag term IDs assigned to the post, as a list of numbers.', + }, + { key: 'comment_status', label: 'Comment Status', value: 'body.comment_status' }, + { key: 'ping_status', label: 'Ping Status', value: 'body.ping_status' }, + ], +}; + +export const createPageActionOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Page ID', value: 'body.id', format: 'number' }, + { key: 'title', label: 'Title', value: 'body.title', children: renderedTextFields }, + { key: 'link', label: 'Link', value: 'body.link', format: 'url' }, + { key: 'slug', label: 'Slug', value: 'body.slug' }, + { key: 'status', label: 'Status', value: 'body.status' }, + { key: 'type', label: 'Type', value: 'body.type' }, + { key: 'date', label: 'Published Date', value: 'body.date', format: 'datetime' }, + { key: 'date_gmt', label: 'Published Date (GMT)', value: 'body.date_gmt', format: 'datetime' }, + { key: 'modified', label: 'Modified Date', value: 'body.modified', format: 'datetime' }, + { key: 'modified_gmt', label: 'Modified Date (GMT)', value: 'body.modified_gmt', format: 'datetime' }, + { key: 'content', label: 'Content', value: 'body.content', children: renderedBodyFields }, + { key: 'excerpt', label: 'Excerpt', value: 'body.excerpt', children: renderedBodyFields }, + { key: 'guid', label: 'GUID', value: 'body.guid', children: renderedUrlFields }, + { key: 'author', label: 'Author ID', value: 'body.author', format: 'number' }, + { key: 'featured_media', label: 'Featured Media ID', value: 'body.featured_media', format: 'number' }, + { key: 'parent', label: 'Parent Page ID', value: 'body.parent', format: 'number' }, + { key: 'menu_order', label: 'Menu Order', value: 'body.menu_order', format: 'number' }, + { key: 'comment_status', label: 'Comment Status', value: 'body.comment_status' }, + { key: 'ping_status', label: 'Ping Status', value: 'body.ping_status' }, + ], +}; + +export const getPostActionOutputSchema: OutputSchema = { + fields: [ + { key: 'id', label: 'Post ID', value: 'body.id', format: 'number' }, + { key: 'title', label: 'Title', value: 'body.title', children: renderedTextFields }, + { key: 'link', label: 'Link', value: 'body.link', format: 'url' }, + { key: 'slug', label: 'Slug', value: 'body.slug' }, + { key: 'status', label: 'Status', value: 'body.status' }, + { key: 'type', label: 'Type', value: 'body.type' }, + { key: 'date', label: 'Published Date', value: 'body.date', format: 'datetime' }, + { key: 'date_gmt', label: 'Published Date (GMT)', value: 'body.date_gmt', format: 'datetime' }, + { key: 'modified', label: 'Modified Date', value: 'body.modified', format: 'datetime' }, + { key: 'modified_gmt', label: 'Modified Date (GMT)', value: 'body.modified_gmt', format: 'datetime' }, + { key: 'content', label: 'Content', value: 'body.content', children: renderedBodyFields }, + { key: 'excerpt', label: 'Excerpt', value: 'body.excerpt', children: renderedBodyFields }, + { key: 'guid', label: 'GUID', value: 'body.guid', children: renderedUrlFields }, + { key: 'author', label: 'Author ID', value: 'body.author', format: 'number' }, + { key: 'featured_media', label: 'Featured Media ID', value: 'body.featured_media', format: 'number' }, + { key: 'sticky', label: 'Sticky', value: 'body.sticky', format: 'boolean' }, + { key: 'format', label: 'Format', value: 'body.format' }, + { + key: 'categories', + label: 'Category IDs', + value: 'body.categories', + description: 'Category term IDs assigned to the post, as a list of numbers.', + }, + { + key: 'tags', + label: 'Tag IDs', + value: 'body.tags', + description: 'Tag term IDs assigned to the post, as a list of numbers.', + }, + { key: 'comment_status', label: 'Comment Status', value: 'body.comment_status' }, + { key: 'ping_status', label: 'Ping Status', value: 'body.ping_status' }, + ], +}; + +const postFields: OutputSchema['fields'] = [ + { key: 'id', label: 'Post ID', format: 'number' }, + { key: 'title', label: 'Title', children: renderedTextFields }, + { key: 'link', label: 'Link', format: 'url' }, + { key: 'slug', label: 'Slug' }, + { key: 'status', label: 'Status' }, + { key: 'type', label: 'Type' }, + { key: 'date', label: 'Published Date', format: 'datetime' }, + { key: 'date_gmt', label: 'Published Date (GMT)', format: 'datetime' }, + { key: 'modified', label: 'Modified Date', format: 'datetime' }, + { key: 'modified_gmt', label: 'Modified Date (GMT)', format: 'datetime' }, + { key: 'content', label: 'Content', children: renderedBodyFields }, + { key: 'excerpt', label: 'Excerpt', children: renderedBodyFields }, + { key: 'guid', label: 'GUID', children: renderedUrlFields }, + { key: 'author', label: 'Author ID', format: 'number' }, + { key: 'featured_media', label: 'Featured Media ID', format: 'number' }, + { key: 'sticky', label: 'Sticky', format: 'boolean' }, + { key: 'format', label: 'Format' }, + { + key: 'categories', + label: 'Category IDs', + description: 'Category term IDs assigned to the post, as a list of numbers.', + }, + { + key: 'tags', + label: 'Tag IDs', + description: 'Tag term IDs assigned to the post, as a list of numbers.', + }, + { key: 'comment_status', label: 'Comment Status' }, + { key: 'ping_status', label: 'Ping Status' }, +]; + +// update_post returns response.body, so its paths are bare — unlike the create/get +// actions above, which return the full HttpResponse and address the post under body. +export const updatePostActionOutputSchema: OutputSchema = { fields: postFields }; + +export const newPostTriggerOutputSchema: OutputSchema = { fields: postFields }; diff --git a/packages/pieces/community/wordpress/src/lib/trigger/new-post.trigger.ts b/packages/pieces/community/wordpress/src/lib/trigger/new-post.trigger.ts index 7883a7797519..b2f37dd8b758 100644 --- a/packages/pieces/community/wordpress/src/lib/trigger/new-post.trigger.ts +++ b/packages/pieces/community/wordpress/src/lib/trigger/new-post.trigger.ts @@ -12,11 +12,13 @@ import { import { wordpressCommon } from '../common'; import dayjs from 'dayjs'; import { wordpressAuth } from '../..'; +import { newPostTriggerOutputSchema } from '../output-schemas'; export const wordpressNewPost = createTrigger({ auth: wordpressAuth, name: 'new_post', displayName: 'New Post', + outputSchema: newPostTriggerOutputSchema, sampleData: { id: 60, date: '2023-02-19T10:08:25',