diff --git a/apps/docs/content/docs/en/tools/hubspot.mdx b/apps/docs/content/docs/en/tools/hubspot.mdx index 99cba9a5343..882f2766f10 100644 --- a/apps/docs/content/docs/en/tools/hubspot.mdx +++ b/apps/docs/content/docs/en/tools/hubspot.mdx @@ -41,6 +41,7 @@ Retrieve all users from HubSpot account | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `limit` | string | No | Number of results to return \(default: 100, max: 100\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | #### Output @@ -53,6 +54,9 @@ Retrieve all users from HubSpot account | ↳ `primaryTeamId` | string | Primary team ID | | ↳ `secondaryTeamIds` | array | Secondary team IDs | | ↳ `superAdmin` | boolean | Whether user is a super admin | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | | `totalItems` | number | Total number of users returned | | `success` | boolean | Operation success status | @@ -230,7 +234,7 @@ Search for contacts in HubSpot using filters, sorting, and queries | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "CONTAINS"\), and "value" | +| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "CONTAINS_TOKEN", "GT"\), and "value" | | `sorts` | array | No | Array of sort objects as JSON with "propertyName" and "direction" \("ASCENDING" or "DESCENDING"\) | | `query` | string | No | Search query string to match against contact name, email, and other text fields | | `properties` | array | No | Array of HubSpot property names to return \(e.g., \["email", "firstname", "lastname", "phone"\]\) | @@ -449,7 +453,7 @@ Search for companies in HubSpot using filters, sorting, and queries | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "CONTAINS"\), and "value" | +| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "CONTAINS_TOKEN", "GT"\), and "value" | | `sorts` | array | No | Array of sort objects as JSON with "propertyName" and "direction" \("ASCENDING" or "DESCENDING"\) | | `query` | string | No | Search query string to match against company name, domain, and other text fields | | `properties` | array | No | Array of HubSpot property names to return \(e.g., \["name", "domain", "industry"\]\) | @@ -529,4 +533,887 @@ Retrieve all deals from HubSpot account with pagination support | ↳ `hasMore` | boolean | Whether more records are available | | `success` | boolean | Operation success status | +### `hubspot_get_deal` + +Retrieve a single deal by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `dealId` | string | Yes | The HubSpot deal ID to retrieve | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "dealname,amount,dealstage"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "contacts,companies"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deal` | object | HubSpot deal record | +| ↳ `dealname` | string | Deal name | +| ↳ `amount` | string | Deal amount | +| ↳ `dealstage` | string | Current deal stage | +| ↳ `pipeline` | string | Pipeline the deal is in | +| ↳ `closedate` | string | Expected close date \(ISO 8601\) | +| ↳ `dealtype` | string | Deal type \(New Business, Existing Business, etc.\) | +| ↳ `description` | string | Deal description | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Deal creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| ↳ `num_associated_contacts` | string | Number of associated contacts | +| `dealId` | string | The retrieved deal ID | +| `success` | boolean | Operation success status | + +### `hubspot_create_deal` + +Create a new deal in HubSpot. Requires at least a dealname property + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `properties` | object | Yes | Deal properties as JSON object. Must include dealname \(e.g., \{"dealname": "New Deal", "amount": "5000", "dealstage": "appointmentscheduled"\}\) | +| `associations` | array | No | Array of associations to create with the deal as JSON. Each object should have "to.id" and "types" array with "associationCategory" and "associationTypeId" | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deal` | object | HubSpot deal record | +| ↳ `dealname` | string | Deal name | +| ↳ `amount` | string | Deal amount | +| ↳ `dealstage` | string | Current deal stage | +| ↳ `pipeline` | string | Pipeline the deal is in | +| ↳ `closedate` | string | Expected close date \(ISO 8601\) | +| ↳ `dealtype` | string | Deal type \(New Business, Existing Business, etc.\) | +| ↳ `description` | string | Deal description | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Deal creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| ↳ `num_associated_contacts` | string | Number of associated contacts | +| `dealId` | string | The created deal ID | +| `success` | boolean | Operation success status | + +### `hubspot_update_deal` + +Update an existing deal in HubSpot by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `dealId` | string | Yes | The HubSpot deal ID to update | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | object | Yes | Deal properties to update as JSON object \(e.g., \{"amount": "10000", "dealstage": "closedwon"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deal` | object | HubSpot deal record | +| ↳ `dealname` | string | Deal name | +| ↳ `amount` | string | Deal amount | +| ↳ `dealstage` | string | Current deal stage | +| ↳ `pipeline` | string | Pipeline the deal is in | +| ↳ `closedate` | string | Expected close date \(ISO 8601\) | +| ↳ `dealtype` | string | Deal type \(New Business, Existing Business, etc.\) | +| ↳ `description` | string | Deal description | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Deal creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| ↳ `num_associated_contacts` | string | Number of associated contacts | +| `dealId` | string | The updated deal ID | +| `success` | boolean | Operation success status | + +### `hubspot_search_deals` + +Search for deals in HubSpot using filters, sorting, and queries + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "NEQ", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN"\), and "value" | +| `sorts` | array | No | Array of sort objects as JSON with "propertyName" and "direction" \("ASCENDING" or "DESCENDING"\) | +| `query` | string | No | Search query string to match against deal name and other text fields | +| `properties` | array | No | Array of HubSpot property names to return \(e.g., \["dealname", "amount", "dealstage"\]\) | +| `limit` | number | No | Maximum number of results to return \(max 200\) | +| `after` | string | No | Pagination cursor for next page \(from previous response\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `deals` | array | Array of HubSpot deal records | +| ↳ `dealname` | string | Deal name | +| ↳ `amount` | string | Deal amount | +| ↳ `dealstage` | string | Current deal stage | +| ↳ `pipeline` | string | Pipeline the deal is in | +| ↳ `closedate` | string | Expected close date \(ISO 8601\) | +| ↳ `dealtype` | string | Deal type \(New Business, Existing Business, etc.\) | +| ↳ `description` | string | Deal description | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Deal creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| ↳ `num_associated_contacts` | string | Number of associated contacts | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `total` | number | Total number of matching deals | +| `success` | boolean | Operation success status | + +### `hubspot_list_tickets` + +Retrieve all tickets from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "subject,content,hs_ticket_priority"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "contacts,companies"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tickets` | array | Array of HubSpot ticket records | +| ↳ `subject` | string | Ticket subject/name | +| ↳ `content` | string | Ticket content/description | +| ↳ `hs_pipeline` | string | Pipeline the ticket is in | +| ↳ `hs_pipeline_stage` | string | Current pipeline stage | +| ↳ `hs_ticket_priority` | string | Ticket priority \(LOW, MEDIUM, HIGH\) | +| ↳ `hs_ticket_category` | string | Ticket category | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Ticket creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_ticket` + +Retrieve a single ticket by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `ticketId` | string | Yes | The HubSpot ticket ID to retrieve | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "subject,content,hs_ticket_priority"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "contacts,companies"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ticket` | object | HubSpot ticket record | +| ↳ `subject` | string | Ticket subject/name | +| ↳ `content` | string | Ticket content/description | +| ↳ `hs_pipeline` | string | Pipeline the ticket is in | +| ↳ `hs_pipeline_stage` | string | Current pipeline stage | +| ↳ `hs_ticket_priority` | string | Ticket priority \(LOW, MEDIUM, HIGH\) | +| ↳ `hs_ticket_category` | string | Ticket category | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Ticket creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `ticketId` | string | The retrieved ticket ID | +| `success` | boolean | Operation success status | + +### `hubspot_create_ticket` + +Create a new ticket in HubSpot. Requires subject and hs_pipeline_stage properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `properties` | object | Yes | Ticket properties as JSON object. Must include subject and hs_pipeline_stage \(e.g., \{"subject": "Support request", "hs_pipeline_stage": "1", "hs_ticket_priority": "HIGH"\}\) | +| `associations` | array | No | Array of associations to create with the ticket as JSON. Each object should have "to.id" and "types" array with "associationCategory" and "associationTypeId" | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ticket` | object | HubSpot ticket record | +| ↳ `subject` | string | Ticket subject/name | +| ↳ `content` | string | Ticket content/description | +| ↳ `hs_pipeline` | string | Pipeline the ticket is in | +| ↳ `hs_pipeline_stage` | string | Current pipeline stage | +| ↳ `hs_ticket_priority` | string | Ticket priority \(LOW, MEDIUM, HIGH\) | +| ↳ `hs_ticket_category` | string | Ticket category | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Ticket creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `ticketId` | string | The created ticket ID | +| `success` | boolean | Operation success status | + +### `hubspot_update_ticket` + +Update an existing ticket in HubSpot by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `ticketId` | string | Yes | The HubSpot ticket ID to update | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | object | Yes | Ticket properties to update as JSON object \(e.g., \{"subject": "Updated subject", "hs_ticket_priority": "HIGH"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ticket` | object | HubSpot ticket record | +| ↳ `subject` | string | Ticket subject/name | +| ↳ `content` | string | Ticket content/description | +| ↳ `hs_pipeline` | string | Pipeline the ticket is in | +| ↳ `hs_pipeline_stage` | string | Current pipeline stage | +| ↳ `hs_ticket_priority` | string | Ticket priority \(LOW, MEDIUM, HIGH\) | +| ↳ `hs_ticket_category` | string | Ticket category | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Ticket creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `ticketId` | string | The updated ticket ID | +| `success` | boolean | Operation success status | + +### `hubspot_search_tickets` + +Search for tickets in HubSpot using filters, sorting, and queries + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `filterGroups` | array | No | Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" \(e.g., "EQ", "NEQ", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN"\), and "value" | +| `sorts` | array | No | Array of sort objects as JSON with "propertyName" and "direction" \("ASCENDING" or "DESCENDING"\) | +| `query` | string | No | Search query string to match against ticket subject and other text fields | +| `properties` | array | No | Array of HubSpot property names to return \(e.g., \["subject", "content", "hs_ticket_priority"\]\) | +| `limit` | number | No | Maximum number of results to return \(max 200\) | +| `after` | string | No | Pagination cursor for next page \(from previous response\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tickets` | array | Array of HubSpot ticket records | +| ↳ `subject` | string | Ticket subject/name | +| ↳ `content` | string | Ticket content/description | +| ↳ `hs_pipeline` | string | Pipeline the ticket is in | +| ↳ `hs_pipeline_stage` | string | Current pipeline stage | +| ↳ `hs_ticket_priority` | string | Ticket priority \(LOW, MEDIUM, HIGH\) | +| ↳ `hs_ticket_category` | string | Ticket category | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Ticket creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `total` | number | Total number of matching tickets | +| `success` | boolean | Operation success status | + +### `hubspot_list_line_items` + +Retrieve all line items from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "name,quantity,price,amount"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "deals,quotes"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lineItems` | array | Array of HubSpot line item records | +| ↳ `name` | string | Line item name | +| ↳ `description` | string | Full description of the product | +| ↳ `hs_sku` | string | Unique product identifier \(SKU\) | +| ↳ `quantity` | string | Number of units included | +| ↳ `price` | string | Unit price | +| ↳ `amount` | string | Total cost \(quantity * unit price\) | +| ↳ `hs_line_item_currency_code` | string | Currency code | +| ↳ `recurringbillingfrequency` | string | Recurring billing frequency | +| ↳ `hs_recurring_billing_start_date` | string | Recurring billing start date | +| ↳ `hs_recurring_billing_end_date` | string | Recurring billing end date | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_line_item` + +Retrieve a single line item by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `lineItemId` | string | Yes | The HubSpot line item ID to retrieve | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "name,quantity,price,amount"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "deals,quotes"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lineItem` | object | HubSpot line item record | +| ↳ `name` | string | Line item name | +| ↳ `description` | string | Full description of the product | +| ↳ `hs_sku` | string | Unique product identifier \(SKU\) | +| ↳ `quantity` | string | Number of units included | +| ↳ `price` | string | Unit price | +| ↳ `amount` | string | Total cost \(quantity * unit price\) | +| ↳ `hs_line_item_currency_code` | string | Currency code | +| ↳ `recurringbillingfrequency` | string | Recurring billing frequency | +| ↳ `hs_recurring_billing_start_date` | string | Recurring billing start date | +| ↳ `hs_recurring_billing_end_date` | string | Recurring billing end date | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `lineItemId` | string | The retrieved line item ID | +| `success` | boolean | Operation success status | + +### `hubspot_create_line_item` + +Create a new line item in HubSpot. Requires at least a name property + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `properties` | object | Yes | Line item properties as JSON object \(e.g., \{"name": "Product A", "quantity": "2", "price": "50.00", "hs_sku": "SKU-001"\}\) | +| `associations` | array | No | Array of associations to create with the line item as JSON. Each object should have "to.id" and "types" array with "associationCategory" and "associationTypeId" | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lineItem` | object | HubSpot line item record | +| ↳ `name` | string | Line item name | +| ↳ `description` | string | Full description of the product | +| ↳ `hs_sku` | string | Unique product identifier \(SKU\) | +| ↳ `quantity` | string | Number of units included | +| ↳ `price` | string | Unit price | +| ↳ `amount` | string | Total cost \(quantity * unit price\) | +| ↳ `hs_line_item_currency_code` | string | Currency code | +| ↳ `recurringbillingfrequency` | string | Recurring billing frequency | +| ↳ `hs_recurring_billing_start_date` | string | Recurring billing start date | +| ↳ `hs_recurring_billing_end_date` | string | Recurring billing end date | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `lineItemId` | string | The created line item ID | +| `success` | boolean | Operation success status | + +### `hubspot_update_line_item` + +Update an existing line item in HubSpot by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `lineItemId` | string | Yes | The HubSpot line item ID to update | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | object | Yes | Line item properties to update as JSON object \(e.g., \{"quantity": "5", "price": "25.00"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lineItem` | object | HubSpot line item record | +| ↳ `name` | string | Line item name | +| ↳ `description` | string | Full description of the product | +| ↳ `hs_sku` | string | Unique product identifier \(SKU\) | +| ↳ `quantity` | string | Number of units included | +| ↳ `price` | string | Unit price | +| ↳ `amount` | string | Total cost \(quantity * unit price\) | +| ↳ `hs_line_item_currency_code` | string | Currency code | +| ↳ `recurringbillingfrequency` | string | Recurring billing frequency | +| ↳ `hs_recurring_billing_start_date` | string | Recurring billing start date | +| ↳ `hs_recurring_billing_end_date` | string | Recurring billing end date | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `lineItemId` | string | The updated line item ID | +| `success` | boolean | Operation success status | + +### `hubspot_list_quotes` + +Retrieve all quotes from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "hs_title,hs_expiration_date,hs_status"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "deals,line_items"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `quotes` | array | Array of HubSpot quote records | +| ↳ `hs_title` | string | Quote name/title | +| ↳ `hs_expiration_date` | string | Expiration date | +| ↳ `hs_status` | string | Quote status | +| ↳ `hs_esign_enabled` | string | Whether e-signatures are enabled | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_quote` + +Retrieve a single quote by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `quoteId` | string | Yes | The HubSpot quote ID to retrieve | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "hs_title,hs_expiration_date,hs_status"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "deals,line_items"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `quote` | object | HubSpot quote record | +| ↳ `hs_title` | string | Quote name/title | +| ↳ `hs_expiration_date` | string | Expiration date | +| ↳ `hs_status` | string | Quote status | +| ↳ `hs_esign_enabled` | string | Whether e-signatures are enabled | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `quoteId` | string | The retrieved quote ID | +| `success` | boolean | Operation success status | + +### `hubspot_list_appointments` + +Retrieve all appointments from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "hs_meeting_title,hs_meeting_start_time"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "contacts,companies"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `appointments` | array | Array of HubSpot appointment records | +| ↳ `hs_appointment_type` | string | Appointment type | +| ↳ `hs_meeting_title` | string | Meeting title | +| ↳ `hs_meeting_start_time` | string | Start time \(ISO 8601\) | +| ↳ `hs_meeting_end_time` | string | End time \(ISO 8601\) | +| ↳ `hs_meeting_location` | string | Meeting location | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `hs_createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_appointment` + +Retrieve a single appointment by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `appointmentId` | string | Yes | The HubSpot appointment ID to retrieve | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | string | No | Comma-separated list of HubSpot property names to return \(e.g., "hs_meeting_title,hs_meeting_start_time"\) | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for \(e.g., "contacts,companies"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `appointment` | object | HubSpot appointment record | +| ↳ `hs_appointment_type` | string | Appointment type | +| ↳ `hs_meeting_title` | string | Meeting title | +| ↳ `hs_meeting_start_time` | string | Start time \(ISO 8601\) | +| ↳ `hs_meeting_end_time` | string | End time \(ISO 8601\) | +| ↳ `hs_meeting_location` | string | Meeting location | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `hs_createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `appointmentId` | string | The retrieved appointment ID | +| `success` | boolean | Operation success status | + +### `hubspot_create_appointment` + +Create a new appointment in HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `properties` | object | Yes | Appointment properties as JSON object \(e.g., \{"hs_meeting_title": "Discovery Call", "hs_meeting_start_time": "2024-01-15T10:00:00Z", "hs_meeting_end_time": "2024-01-15T11:00:00Z"\}\) | +| `associations` | array | No | Array of associations to create with the appointment as JSON. Each object should have "to.id" and "types" array with "associationCategory" and "associationTypeId" | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `appointment` | object | HubSpot appointment record | +| ↳ `hs_appointment_type` | string | Appointment type | +| ↳ `hs_meeting_title` | string | Meeting title | +| ↳ `hs_meeting_start_time` | string | Start time \(ISO 8601\) | +| ↳ `hs_meeting_end_time` | string | End time \(ISO 8601\) | +| ↳ `hs_meeting_location` | string | Meeting location | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `hs_createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `appointmentId` | string | The created appointment ID | +| `success` | boolean | Operation success status | + +### `hubspot_update_appointment` + +Update an existing appointment in HubSpot by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `appointmentId` | string | Yes | The HubSpot appointment ID to update | +| `idProperty` | string | No | Property to use as unique identifier. If not specified, uses record ID | +| `properties` | object | Yes | Appointment properties to update as JSON object \(e.g., \{"hs_meeting_title": "Updated Call", "hs_meeting_location": "Zoom"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `appointment` | object | HubSpot appointment record | +| ↳ `hs_appointment_type` | string | Appointment type | +| ↳ `hs_meeting_title` | string | Meeting title | +| ↳ `hs_meeting_start_time` | string | Start time \(ISO 8601\) | +| ↳ `hs_meeting_end_time` | string | End time \(ISO 8601\) | +| ↳ `hs_meeting_location` | string | Meeting location | +| ↳ `hubspot_owner_id` | string | HubSpot owner ID | +| ↳ `hs_object_id` | string | HubSpot object ID \(same as record ID\) | +| ↳ `hs_createdate` | string | Creation date \(ISO 8601\) | +| ↳ `hs_lastmodifieddate` | string | Last modified date \(ISO 8601\) | +| `appointmentId` | string | The updated appointment ID | +| `success` | boolean | Operation success status | + +### `hubspot_list_carts` + +Retrieve all carts from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `properties` | string | No | Comma-separated list of HubSpot property names to return | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `carts` | array | Array of HubSpot CRM records | +| ↳ `id` | string | Unique record ID \(hs_object_id\) | +| ↳ `createdAt` | string | Record creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Record last updated timestamp \(ISO 8601\) | +| ↳ `archived` | boolean | Whether the record is archived | +| ↳ `properties` | object | Record properties | +| ↳ `associations` | object | Associated records | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_cart` + +Retrieve a single cart by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `cartId` | string | Yes | The HubSpot cart ID to retrieve | +| `properties` | string | No | Comma-separated list of HubSpot property names to return | +| `associations` | string | No | Comma-separated list of object types to retrieve associated IDs for | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `cart` | object | HubSpot CRM record | +| ↳ `id` | string | Unique record ID \(hs_object_id\) | +| ↳ `createdAt` | string | Record creation timestamp \(ISO 8601\) | +| ↳ `updatedAt` | string | Record last updated timestamp \(ISO 8601\) | +| ↳ `archived` | boolean | Whether the record is archived | +| ↳ `properties` | object | Record properties | +| ↳ `associations` | object | Associated records | +| `cartId` | string | The retrieved cart ID | +| `success` | boolean | Operation success status | + +### `hubspot_list_owners` + +Retrieve all owners from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 100\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | +| `email` | string | No | Filter owners by email address | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `owners` | array | Array of HubSpot owner objects | +| ↳ `id` | string | Owner ID | +| ↳ `email` | string | Owner email address | +| ↳ `firstName` | string | Owner first name | +| ↳ `lastName` | string | Owner last name | +| ↳ `userId` | number | Associated user ID | +| ↳ `teams` | array | Teams the owner belongs to | +| ↳ `id` | string | Team ID | +| ↳ `name` | string | Team name | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| ↳ `archived` | boolean | Whether the owner is archived | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_list_marketing_events` + +Retrieve all marketing events from HubSpot account with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `limit` | string | No | Maximum number of results per page \(max 100, default 10\) | +| `after` | string | No | Pagination cursor for next page of results \(from previous response\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `events` | array | Array of HubSpot marketing event objects | +| ↳ `objectId` | string | Unique event ID \(HubSpot internal\) | +| ↳ `eventName` | string | Event name | +| ↳ `eventType` | string | Event type | +| ↳ `eventStatus` | string | Event status | +| ↳ `eventDescription` | string | Event description | +| ↳ `eventUrl` | string | Event URL | +| ↳ `eventOrganizer` | string | Event organizer | +| ↳ `startDateTime` | string | Start date/time \(ISO 8601\) | +| ↳ `endDateTime` | string | End date/time \(ISO 8601\) | +| ↳ `eventCancelled` | boolean | Whether event is cancelled | +| ↳ `eventCompleted` | boolean | Whether event is completed | +| ↳ `registrants` | number | Number of registrants | +| ↳ `attendees` | number | Number of attendees | +| ↳ `cancellations` | number | Number of cancellations | +| ↳ `noShows` | number | Number of no-shows | +| ↳ `externalEventId` | string | External event ID | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| `success` | boolean | Operation success status | + +### `hubspot_get_marketing_event` + +Retrieve a single marketing event by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `eventId` | string | Yes | The HubSpot marketing event objectId to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `event` | object | HubSpot marketing event | +| ↳ `objectId` | string | Unique event ID \(HubSpot internal\) | +| ↳ `eventName` | string | Event name | +| ↳ `eventType` | string | Event type | +| ↳ `eventStatus` | string | Event status | +| ↳ `eventDescription` | string | Event description | +| ↳ `eventUrl` | string | Event URL | +| ↳ `eventOrganizer` | string | Event organizer | +| ↳ `startDateTime` | string | Start date/time \(ISO 8601\) | +| ↳ `endDateTime` | string | End date/time \(ISO 8601\) | +| ↳ `eventCancelled` | boolean | Whether event is cancelled | +| ↳ `eventCompleted` | boolean | Whether event is completed | +| ↳ `registrants` | number | Number of registrants | +| ↳ `attendees` | number | Number of attendees | +| ↳ `cancellations` | number | Number of cancellations | +| ↳ `noShows` | number | Number of no-shows | +| ↳ `externalEventId` | string | External event ID | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| `eventId` | string | The retrieved marketing event ID | +| `success` | boolean | Operation success status | + +### `hubspot_list_lists` + +Search and retrieve lists from HubSpot account + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `query` | string | No | Search query to filter lists by name. Leave empty to return all lists. | +| `count` | string | No | Maximum number of results to return \(default 20, max 500\) | +| `offset` | string | No | Pagination offset for next page of results \(use the offset value from previous response\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `lists` | array | Array of HubSpot list objects | +| ↳ `listId` | string | List ID | +| ↳ `name` | string | List name | +| ↳ `objectTypeId` | string | Object type ID \(e.g., 0-1 for contacts\) | +| ↳ `processingType` | string | Processing type \(MANUAL, DYNAMIC, SNAPSHOT\) | +| ↳ `processingStatus` | string | Processing status \(COMPLETE, PROCESSING\) | +| ↳ `listVersion` | number | List version number | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| `paging` | object | Pagination information for fetching more results | +| ↳ `after` | string | Cursor for next page of results | +| ↳ `link` | string | Link to next page | +| `metadata` | object | Response metadata | +| ↳ `totalReturned` | number | Number of records returned in this response | +| ↳ `hasMore` | boolean | Whether more records are available | +| ↳ `total` | number | Total number of lists matching the query | +| `success` | boolean | Operation success status | + +### `hubspot_get_list` + +Retrieve a single list by ID from HubSpot + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `listId` | string | Yes | The HubSpot list ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `list` | object | HubSpot list | +| ↳ `listId` | string | List ID | +| ↳ `name` | string | List name | +| ↳ `objectTypeId` | string | Object type ID \(e.g., 0-1 for contacts\) | +| ↳ `processingType` | string | Processing type \(MANUAL, DYNAMIC, SNAPSHOT\) | +| ↳ `processingStatus` | string | Processing status \(COMPLETE, PROCESSING\) | +| ↳ `listVersion` | number | List version number | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| `listId` | string | The retrieved list ID | +| `success` | boolean | Operation success status | + +### `hubspot_create_list` + +Create a new list in HubSpot. Specify the object type and processing type (MANUAL or DYNAMIC) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `name` | string | Yes | Name of the list | +| `objectTypeId` | string | Yes | Object type ID \(e.g., "0-1" for contacts, "0-2" for companies\) | +| `processingType` | string | Yes | Processing type: "MANUAL" for static lists or "DYNAMIC" for active lists | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `list` | object | HubSpot list | +| ↳ `listId` | string | List ID | +| ↳ `name` | string | List name | +| ↳ `objectTypeId` | string | Object type ID \(e.g., 0-1 for contacts\) | +| ↳ `processingType` | string | Processing type \(MANUAL, DYNAMIC, SNAPSHOT\) | +| ↳ `processingStatus` | string | Processing status \(COMPLETE, PROCESSING\) | +| ↳ `listVersion` | number | List version number | +| ↳ `createdAt` | string | Creation date \(ISO 8601\) | +| ↳ `updatedAt` | string | Last updated date \(ISO 8601\) | +| `listId` | string | The created list ID | +| `success` | boolean | Operation success status | + diff --git a/apps/docs/content/docs/en/tools/rippling.mdx b/apps/docs/content/docs/en/tools/rippling.mdx index 6eecade9a09..b029318cf27 100644 --- a/apps/docs/content/docs/en/tools/rippling.mdx +++ b/apps/docs/content/docs/en/tools/rippling.mdx @@ -72,7 +72,7 @@ List all employees in Rippling with optional pagination | ↳ `endDate` | string | Employment end date | | ↳ `manager` | string | Manager ID or name | | ↳ `phone` | string | Phone number | -| `totalCount` | number | Total number of employees returned | +| `totalCount` | number | Number of employees returned on this page | ### `rippling_get_employee` @@ -131,7 +131,7 @@ List all employees in Rippling including terminated employees with optional pagi | ↳ `endDate` | string | Employment end date | | ↳ `manager` | string | Manager ID or name | | ↳ `phone` | string | Phone number | -| `totalCount` | number | Total number of employees returned | +| `totalCount` | number | Number of employees returned on this page | ### `rippling_list_departments` @@ -153,7 +153,7 @@ List all departments in the Rippling organization | ↳ `id` | string | Department ID | | ↳ `name` | string | Department name | | ↳ `parent` | string | Parent department ID | -| `totalCount` | number | Total number of departments returned | +| `totalCount` | number | Number of departments returned on this page | ### `rippling_list_teams` @@ -175,7 +175,7 @@ List all teams in Rippling | ↳ `id` | string | Team ID | | ↳ `name` | string | Team name | | ↳ `parent` | string | Parent team ID | -| `totalCount` | number | Total number of teams returned | +| `totalCount` | number | Number of teams returned on this page | ### `rippling_list_levels` @@ -197,7 +197,7 @@ List all position levels in Rippling | ↳ `id` | string | Level ID | | ↳ `name` | string | Level name | | ↳ `parent` | string | Parent level ID | -| `totalCount` | number | Total number of levels returned | +| `totalCount` | number | Number of levels returned on this page | ### `rippling_list_work_locations` @@ -223,7 +223,7 @@ List all work locations in Rippling | ↳ `state` | string | State or province | | ↳ `zip` | string | ZIP or postal code | | ↳ `country` | string | Country | -| `totalCount` | number | Total number of work locations returned | +| `totalCount` | number | Number of work locations returned on this page | ### `rippling_get_company` @@ -270,7 +270,7 @@ Get activity events for the current company in Rippling | ↳ `description` | string | Event description | | ↳ `createdAt` | string | Event creation timestamp | | ↳ `actor` | json | Actor who triggered the event \(id, name\) | -| `totalCount` | number | Total number of activity events returned | +| `totalCount` | number | Number of activity events returned on this page | | `nextCursor` | string | Cursor for fetching the next page of results | ### `rippling_list_custom_fields` @@ -294,7 +294,7 @@ List all custom fields defined in Rippling | ↳ `type` | string | Field type | | ↳ `title` | string | Field title | | ↳ `mandatory` | boolean | Whether the field is mandatory | -| `totalCount` | number | Total number of custom fields returned | +| `totalCount` | number | Number of custom fields returned on this page | ### `rippling_get_current_user` @@ -385,7 +385,7 @@ List leave balances for all employees in Rippling | ↳ `balances` | array | Leave balance entries | | ↳ `leaveType` | string | Type of leave | | ↳ `minutesRemaining` | number | Minutes of leave remaining | -| `totalCount` | number | Total number of leave balances returned | +| `totalCount` | number | Number of leave balances returned on this page | ### `rippling_get_leave_balance` diff --git a/apps/sim/app/(home)/components/demo-request/consts.ts b/apps/sim/app/(home)/components/demo-request/consts.ts new file mode 100644 index 00000000000..fadfdd389a9 --- /dev/null +++ b/apps/sim/app/(home)/components/demo-request/consts.ts @@ -0,0 +1,84 @@ +import { z } from 'zod' +import { NO_EMAIL_HEADER_CONTROL_CHARS_REGEX } from '@/lib/messaging/email/utils' +import { quickValidateEmail } from '@/lib/messaging/email/validation' + +export const DEMO_REQUEST_REGION_VALUES = [ + 'north_america', + 'europe', + 'asia_pacific', + 'latin_america', + 'middle_east_africa', + 'other', +] as const + +export const DEMO_REQUEST_USER_COUNT_VALUES = [ + '1_10', + '11_50', + '51_200', + '201_500', + '501_1000', + '1000_plus', +] as const + +export const DEMO_REQUEST_REGION_OPTIONS = [ + { value: 'north_america', label: 'North America' }, + { value: 'europe', label: 'Europe' }, + { value: 'asia_pacific', label: 'Asia Pacific' }, + { value: 'latin_america', label: 'Latin America' }, + { value: 'middle_east_africa', label: 'Middle East & Africa' }, + { value: 'other', label: 'Other' }, +] as const + +export const DEMO_REQUEST_USER_COUNT_OPTIONS = [ + { value: '1_10', label: '1-10' }, + { value: '11_50', label: '11-50' }, + { value: '51_200', label: '51-200' }, + { value: '201_500', label: '201-500' }, + { value: '501_1000', label: '501-1,000' }, + { value: '1000_plus', label: '1,000+' }, +] as const + +export const demoRequestSchema = z.object({ + firstName: z + .string() + .trim() + .min(1, 'First name is required') + .max(100) + .regex(NO_EMAIL_HEADER_CONTROL_CHARS_REGEX, 'Invalid characters'), + lastName: z + .string() + .trim() + .min(1, 'Last name is required') + .max(100) + .regex(NO_EMAIL_HEADER_CONTROL_CHARS_REGEX, 'Invalid characters'), + companyEmail: z + .string() + .trim() + .min(1, 'Company email is required') + .max(320) + .transform((value) => value.toLowerCase()) + .refine((value) => quickValidateEmail(value).isValid, 'Enter a valid work email'), + phoneNumber: z + .string() + .trim() + .max(50, 'Phone number must be 50 characters or less') + .optional() + .transform((value) => (value && value.length > 0 ? value : undefined)), + region: z.enum(DEMO_REQUEST_REGION_VALUES, { + errorMap: () => ({ message: 'Please select a region' }), + }), + userCount: z.enum(DEMO_REQUEST_USER_COUNT_VALUES, { + errorMap: () => ({ message: 'Please select the number of users' }), + }), + details: z.string().trim().min(1, 'Details are required').max(2000), +}) + +export type DemoRequestPayload = z.infer + +export function getDemoRequestRegionLabel(value: DemoRequestPayload['region']): string { + return DEMO_REQUEST_REGION_OPTIONS.find((option) => option.value === value)?.label ?? value +} + +export function getDemoRequestUserCountLabel(value: DemoRequestPayload['userCount']): string { + return DEMO_REQUEST_USER_COUNT_OPTIONS.find((option) => option.value === value)?.label ?? value +} diff --git a/apps/sim/app/(home)/components/demo-request/demo-request-modal.tsx b/apps/sim/app/(home)/components/demo-request/demo-request-modal.tsx new file mode 100644 index 00000000000..0d5e7ff9ed8 --- /dev/null +++ b/apps/sim/app/(home)/components/demo-request/demo-request-modal.tsx @@ -0,0 +1,298 @@ +'use client' + +import { useCallback, useState } from 'react' +import { + Button, + Combobox, + FormField, + Input, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + ModalTrigger, + Textarea, +} from '@/components/emcn' +import { Check } from '@/components/emcn/icons' +import { + DEMO_REQUEST_REGION_OPTIONS, + DEMO_REQUEST_USER_COUNT_OPTIONS, + type DemoRequestPayload, + demoRequestSchema, +} from '@/app/(home)/components/demo-request/consts' + +interface DemoRequestModalProps { + children: React.ReactNode + theme?: 'dark' | 'light' +} + +type DemoRequestField = keyof DemoRequestPayload +type DemoRequestErrors = Partial> + +interface DemoRequestFormState { + firstName: string + lastName: string + companyEmail: string + phoneNumber: string + region: DemoRequestPayload['region'] | '' + userCount: DemoRequestPayload['userCount'] | '' + details: string +} + +const SUBMIT_SUCCESS_MESSAGE = "We'll be in touch soon!" +const COMBOBOX_REGIONS = [...DEMO_REQUEST_REGION_OPTIONS] +const COMBOBOX_USER_COUNTS = [...DEMO_REQUEST_USER_COUNT_OPTIONS] + +const INITIAL_FORM_STATE: DemoRequestFormState = { + firstName: '', + lastName: '', + companyEmail: '', + phoneNumber: '', + region: '', + userCount: '', + details: '', +} + +export function DemoRequestModal({ children, theme = 'dark' }: DemoRequestModalProps) { + const [open, setOpen] = useState(false) + const [form, setForm] = useState(INITIAL_FORM_STATE) + const [errors, setErrors] = useState({}) + const [isSubmitting, setIsSubmitting] = useState(false) + const [submitError, setSubmitError] = useState(null) + const [submitSuccess, setSubmitSuccess] = useState(false) + + const resetForm = useCallback(() => { + setForm(INITIAL_FORM_STATE) + setErrors({}) + setIsSubmitting(false) + setSubmitError(null) + setSubmitSuccess(false) + }, []) + + const handleOpenChange = useCallback( + (nextOpen: boolean) => { + setOpen(nextOpen) + resetForm() + }, + [resetForm] + ) + + const updateField = useCallback( + ( + field: TField, + value: DemoRequestFormState[TField] + ) => { + setForm((prev) => ({ ...prev, [field]: value })) + setErrors((prev) => { + if (!prev[field]) { + return prev + } + + const nextErrors = { ...prev } + delete nextErrors[field] + return nextErrors + }) + setSubmitError(null) + setSubmitSuccess(false) + }, + [] + ) + + const handleSubmit = useCallback( + async (event: React.FormEvent) => { + event.preventDefault() + setSubmitError(null) + setSubmitSuccess(false) + + const parsed = demoRequestSchema.safeParse({ + ...form, + phoneNumber: form.phoneNumber || undefined, + }) + + if (!parsed.success) { + const fieldErrors = parsed.error.flatten().fieldErrors + setErrors({ + firstName: fieldErrors.firstName?.[0], + lastName: fieldErrors.lastName?.[0], + companyEmail: fieldErrors.companyEmail?.[0], + phoneNumber: fieldErrors.phoneNumber?.[0], + region: fieldErrors.region?.[0], + userCount: fieldErrors.userCount?.[0], + details: fieldErrors.details?.[0], + }) + return + } + + setIsSubmitting(true) + + try { + const response = await fetch('/api/demo-requests', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(parsed.data), + }) + + const result = (await response.json().catch(() => null)) as { + error?: string + message?: string + } | null + + if (!response.ok) { + throw new Error(result?.error || 'Failed to submit demo request') + } + + setSubmitSuccess(true) + } catch (error) { + setSubmitError( + error instanceof Error + ? error.message + : 'Failed to submit demo request. Please try again.' + ) + } finally { + setIsSubmitting(false) + } + }, + [form, resetForm] + ) + + return ( + + {children} + + + + {submitSuccess ? 'Demo request submitted' : 'Nearly there!'} + + +
+
+ +
+
+ + updateField('firstName', event.target.value)} + placeholder='First' + /> + + + updateField('lastName', event.target.value)} + placeholder='Last' + /> + +
+ + + updateField('companyEmail', event.target.value)} + placeholder='Your work email' + /> + + + + updateField('phoneNumber', event.target.value)} + placeholder='Your phone number' + /> + + +
+ + + updateField('region', value as DemoRequestPayload['region']) + } + placeholder='Select' + editable={false} + filterOptions={false} + /> + + + + updateField('userCount', value as DemoRequestPayload['userCount']) + } + placeholder='Select' + editable={false} + filterOptions={false} + /> + +
+ + +