diff --git a/flows.yaml b/flows.yaml
index 0b08a3a2..8d993e4c 100644
--- a/flows.yaml
+++ b/flows.yaml
@@ -2384,56 +2384,134 @@ integrations:
description: |
Fetches a list of conversations from Intercom
output:
- - IntercomConversation
- - IntercomConversationMessage
+ - Conversation
+ - ConversationMessage
sync_type: incremental
+ version: 1.0.0
endpoint:
- - GET /intercom/conversations
- - GET /intercom/conversations-message
+ - GET /conversations
+ - GET /conversation-messages
contacts:
runs: every 6 hours
description: |
Fetches a list of contacts from Intercom
- output: IntercomContact
+ output: Contact
+ sync_type: full
+ track_deletes: true
+ version: 1.0.0
+ endpoint: GET /contacts
+ articles:
+ runs: every 6 hours
+ description: |
+ Fetches a list of articles from Intercom
+ output: Article
sync_type: full
- endpoint: GET /intercom/contacts
+ track_deletes: true
+ version: 1.0.0
+ endpoint: GET /articles
models:
- IntercomContact:
+ Contact:
id: string
workspace_id: string
- external_id: string
+ external_id: string | null
type: string
- email: string | null
+ email: string
phone: string | null
name: string | null
- created_at: date
- updated_at: date
- last_seen_at: date | null
- last_replied_at: date | null
- IntercomConversation:
+ created_at: string
+ updated_at: string
+ last_seen_at: string | null
+ last_replied_at: string | null
+ Conversation:
id: string
- created_at: date
- updated_at: date
- waiting_since: date | null
- snoozed_until: date | null
- title: string
+ created_at: string
+ updated_at: string
+ waiting_since: string | null
+ snoozed_until: string | null
+ title: string | null
contacts:
- contact_id: string
state: string
open: boolean
read: boolean
priority: string
- IntercomConversationMessage:
+ ConversationMessage:
id: string
conversation_id: string
body: string
type: string
- created_at: date
- updated_at: date | null
+ created_at: string
+ updated_at: string
author:
type: string
name: string
id: string
+ Article:
+ type: string
+ id: string
+ workspace_id: string
+ title: string
+ description: string | null
+ body: string | null
+ author_id: number
+ state: string
+ created_at: string
+ updated_at: string
+ url: string | null
+ parent_id: number | null
+ parent_ids: number[]
+ parent_type: string | null
+ default_locale?: string | undefined
+ translated_content?: TranslatedContent | null | undefined
+ ArticleContent:
+ type: string | null
+ title: string
+ description: string
+ body: string
+ author_id: number
+ state: string
+ created_at: number
+ updated_at: number
+ url: string
+ TranslatedContent:
+ type: string | null
+ ar: ArticleContent | null
+ bg: ArticleContent | null
+ bs: ArticleContent | null
+ ca: ArticleContent | null
+ cs: ArticleContent | null
+ da: ArticleContent | null
+ de: ArticleContent | null
+ el: ArticleContent | null
+ en: ArticleContent | null
+ es: ArticleContent | null
+ et: ArticleContent | null
+ fi: ArticleContent | null
+ fr: ArticleContent | null
+ he: ArticleContent | null
+ hr: ArticleContent | null
+ hu: ArticleContent | null
+ id: ArticleContent | null
+ it: ArticleContent | null
+ ja: ArticleContent | null
+ ko: ArticleContent | null
+ lt: ArticleContent | null
+ lv: ArticleContent | null
+ mn: ArticleContent | null
+ nb: ArticleContent | null
+ nl: ArticleContent | null
+ pl: ArticleContent | null
+ pt: ArticleContent | null
+ ro: ArticleContent | null
+ ru: ArticleContent | null
+ sl: ArticleContent | null
+ sr: ArticleContent | null
+ sv: ArticleContent | null
+ tr: ArticleContent | null
+ vi: ArticleContent | null
+ pt-BR: ArticleContent | null
+ zh-CN: ArticleContent | null
+ zh-TW: ArticleContent | null
jira:
syncs:
issues:
diff --git a/integrations/calendly/mocks/event-invitees/batchDelete.json b/integrations/calendly/mocks/event-invitees/EventInvitee/batchDelete.json
similarity index 100%
rename from integrations/calendly/mocks/event-invitees/batchDelete.json
rename to integrations/calendly/mocks/event-invitees/EventInvitee/batchDelete.json
diff --git a/integrations/calendly/mocks/event-invitees/batchSave.json b/integrations/calendly/mocks/event-invitees/EventInvitee/batchSave.json
similarity index 100%
rename from integrations/calendly/mocks/event-invitees/batchSave.json
rename to integrations/calendly/mocks/event-invitees/EventInvitee/batchSave.json
diff --git a/integrations/calendly/mocks/event-types/batchDelete.json b/integrations/calendly/mocks/event-types/EventType/batchDelete.json
similarity index 100%
rename from integrations/calendly/mocks/event-types/batchDelete.json
rename to integrations/calendly/mocks/event-types/EventType/batchDelete.json
diff --git a/integrations/calendly/mocks/event-types/batchSave.json b/integrations/calendly/mocks/event-types/EventType/batchSave.json
similarity index 100%
rename from integrations/calendly/mocks/event-types/batchSave.json
rename to integrations/calendly/mocks/event-types/EventType/batchSave.json
diff --git a/integrations/calendly/mocks/events/batchDelete.json b/integrations/calendly/mocks/events/Event/batchDelete.json
similarity index 100%
rename from integrations/calendly/mocks/events/batchDelete.json
rename to integrations/calendly/mocks/events/Event/batchDelete.json
diff --git a/integrations/calendly/mocks/events/batchSave.json b/integrations/calendly/mocks/events/Event/batchSave.json
similarity index 100%
rename from integrations/calendly/mocks/events/batchSave.json
rename to integrations/calendly/mocks/events/Event/batchSave.json
diff --git a/integrations/calendly/tests/calendly-event-invitees.test.ts b/integrations/calendly/tests/calendly-event-invitees.test.ts
index b542655c..8026ba3e 100644
--- a/integrations/calendly/tests/calendly-event-invitees.test.ts
+++ b/integrations/calendly/tests/calendly-event-invitees.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/event-invitees.js";
@@ -10,39 +9,45 @@ describe("calendly event-invitees tests", () => {
Model: "EventInvitee"
});
+ const models = "EventInvitee".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "EventInvitee");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "EventInvitee");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/calendly/tests/calendly-event-types.test.ts b/integrations/calendly/tests/calendly-event-types.test.ts
index f6d5c461..2009a8e5 100644
--- a/integrations/calendly/tests/calendly-event-types.test.ts
+++ b/integrations/calendly/tests/calendly-event-types.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/event-types.js";
@@ -10,39 +9,45 @@ describe("calendly event-types tests", () => {
Model: "EventType"
});
+ const models = "EventType".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "EventType");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "EventType");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/calendly/tests/calendly-events.test.ts b/integrations/calendly/tests/calendly-events.test.ts
index 63e6b155..772560ab 100644
--- a/integrations/calendly/tests/calendly-events.test.ts
+++ b/integrations/calendly/tests/calendly-events.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/events.js";
@@ -10,39 +9,45 @@ describe("calendly events tests", () => {
Model: "Event"
});
+ const models = "Event".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Event");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Event");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/intercom/mappers/to-article.ts b/integrations/intercom/mappers/to-article.ts
new file mode 100644
index 00000000..7869be1c
--- /dev/null
+++ b/integrations/intercom/mappers/to-article.ts
@@ -0,0 +1,28 @@
+import type { IntercomArticle } from '../types';
+import type { Article } from '../../models';
+/**
+ * Maps an IntercomArticle object to an Article object
+ *
+ * @param article The IntercomArticle object to convert.
+ * @returns Article object representing IntercomArticle article information.
+ */
+export function toArticle(article: IntercomArticle): Article {
+ return {
+ type: article.type,
+ id: article.id,
+ workspace_id: article.workspace_id,
+ title: article.title,
+ description: article.description,
+ body: article.body,
+ author_id: article.author_id,
+ state: article.state,
+ created_at: new Date(article.created_at * 1000).toISOString(),
+ updated_at: new Date(article.updated_at * 1000).toISOString(),
+ url: article.url,
+ parent_id: article.parent_id,
+ parent_ids: article.parent_ids,
+ parent_type: article.parent_type,
+ default_locale: article.default_locale,
+ translated_content: article.translated_content
+ };
+}
diff --git a/integrations/intercom/mappers/to-contact.ts b/integrations/intercom/mappers/to-contact.ts
new file mode 100644
index 00000000..acd5c1ff
--- /dev/null
+++ b/integrations/intercom/mappers/to-contact.ts
@@ -0,0 +1,24 @@
+import type { Contact } from '../../models';
+import type { IntercomContact } from '../types';
+
+/**
+ * Maps an Intercom API contact object to an Contact object.
+ *
+ * @param contact The raw contact object from the Intercom API.
+ * @returns Mapped Contact object with essential properties.
+ */
+export function toContact(contact: IntercomContact): Contact {
+ return {
+ id: contact.id,
+ workspace_id: contact.workspace_id,
+ external_id: contact.external_id,
+ type: contact.role,
+ email: contact.email,
+ phone: contact.phone,
+ name: contact.name,
+ created_at: new Date(contact.created_at * 1000).toISOString(),
+ updated_at: new Date(contact.updated_at * 1000).toISOString(),
+ last_seen_at: contact.last_seen_at ? new Date(contact.last_seen_at * 1000).toISOString() : null,
+ last_replied_at: contact.last_replied_at ? new Date(contact.last_replied_at * 1000).toISOString() : null
+ };
+}
diff --git a/integrations/intercom/mappers/to-conversation.ts b/integrations/intercom/mappers/to-conversation.ts
new file mode 100644
index 00000000..4f5c35d0
--- /dev/null
+++ b/integrations/intercom/mappers/to-conversation.ts
@@ -0,0 +1,88 @@
+import type { Conversation, ConversationMessage } from '../../models';
+import type { IntercomConversation, IntercomConversationMessage, Contact } from '../types';
+
+/**
+ * Maps raw conversation data from Intercom to Conversation model.
+ *
+ * @param conversationResp - The raw API response for a conversation.
+ * @returns Conversation - Mapped conversation object.
+ */
+export function mapConversation(conversationResp: IntercomConversation): Conversation {
+ return {
+ id: conversationResp.id,
+ created_at: new Date(conversationResp.created_at * 1000).toISOString(),
+ updated_at: new Date(conversationResp.updated_at * 1000).toISOString(),
+ waiting_since: conversationResp.waiting_since ? new Date(conversationResp.waiting_since * 1000).toISOString() : null,
+ snoozed_until: conversationResp.snoozed_until ? new Date(conversationResp.snoozed_until * 1000).toISOString() : null,
+ title: conversationResp.title,
+ contacts: conversationResp.contacts.contacts.map((contact: Contact) => ({
+ contact_id: contact.id
+ })),
+ state: conversationResp.state,
+ open: conversationResp.open,
+ read: conversationResp.read,
+ priority: conversationResp.priority
+ };
+}
+
+/**
+ * Maps raw conversation message data from Intercom to ConversationMessage model.
+ *
+ * @param messageResp - The raw API response for a message.
+ * @returns ConversationMessage[] - List of mapped message objects.
+ */
+export function mapMessages(messageResp: IntercomConversationMessage): ConversationMessage[] {
+ const messages: ConversationMessage[] = [];
+
+ messages.push({
+ id: messageResp.source.id,
+ conversation_id: messageResp.id,
+ body: messageResp.source.body,
+ type: 'comment',
+ created_at: new Date(messageResp.created_at * 1000).toISOString(),
+ updated_at: new Date(messageResp.updated_at * 1000).toISOString(),
+ author: {
+ type: mapAuthorType(messageResp.source.author.type),
+ name: messageResp.source.author.name,
+ id: messageResp.source.author.id
+ }
+ });
+
+ for (const conversationPart of messageResp.conversation_parts.conversation_parts) {
+ if (conversationPart.body === null) {
+ continue;
+ }
+
+ messages.push({
+ id: conversationPart.id,
+ conversation_id: messageResp.id,
+ body: conversationPart.body,
+ type: mapMessagePartType(conversationPart.part_type),
+ created_at: new Date(conversationPart.created_at * 1000).toISOString(),
+ updated_at: new Date(conversationPart.updated_at * 1000).toISOString(),
+ author: {
+ type: mapAuthorType(conversationPart.author.type),
+ name: conversationPart.author.name,
+ id: conversationPart.author.id
+ }
+ });
+ }
+
+ return messages;
+}
+
+function mapMessagePartType(rawType: string): string {
+ if (rawType === 'assignment') {
+ return 'comment';
+ }
+ return rawType;
+}
+
+function mapAuthorType(rawType: string): string {
+ if (rawType === 'team') {
+ return 'admin';
+ } else if (rawType === 'lead') {
+ return 'user';
+ }
+ return rawType;
+}
diff --git a/integrations/notion/mocks/content-metadata/batchDelete.json b/integrations/intercom/mocks/articles/Article/batchDelete.json
similarity index 100%
rename from integrations/notion/mocks/content-metadata/batchDelete.json
rename to integrations/intercom/mocks/articles/Article/batchDelete.json
diff --git a/integrations/intercom/mocks/articles/Article/batchSave.json b/integrations/intercom/mocks/articles/Article/batchSave.json
new file mode 100644
index 00000000..bff655dc
--- /dev/null
+++ b/integrations/intercom/mocks/articles/Article/batchSave.json
@@ -0,0 +1,98 @@
+[
+ {
+ "type": "article",
+ "id": "9939230",
+ "workspace_id": "s0lour9i",
+ "title": "Lorem text",
+ "description": "More Test",
+ "body": "
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": "2024-09-30T14:42:52.000Z",
+ "updated_at": "2024-09-30T14:43:44.000Z",
+ "url": "https://intercom.help/nango/en/articles/9939230-lorem-text",
+ "parent_id": null,
+ "parent_ids": [10521524],
+ "parent_type": null
+ },
+ {
+ "type": "article",
+ "id": "9939225",
+ "workspace_id": "s0lour9i",
+ "title": "Lorem",
+ "description": "Another lorem",
+ "body": "Blorf quaxle fimpedoo, snerk whizzleplop! Glibber wompus frindle blargh, zindle skroop. Jibber jabber wibbly wobbly, froodlesnorp quibbly floop. Squeegee munchkin, vleep fubbit, and wooshy flangle bork. Doodlewhap quarnish, yorp splinkledorf, flumple wizzle. Higgledy piggledy, snorpwobble greeble plonkit.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": "2024-09-30T14:41:56.000Z",
+ "updated_at": "2024-09-30T14:42:43.000Z",
+ "url": "https://intercom.help/nango/en/articles/9939225-lorem",
+ "parent_id": null,
+ "parent_ids": [10521524],
+ "parent_type": null
+ },
+ {
+ "type": "article",
+ "id": "9939221",
+ "workspace_id": "s0lour9i",
+ "title": "Test3",
+ "description": "Lorem ipsum dolor",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": "2024-09-30T14:40:57.000Z",
+ "updated_at": "2024-09-30T14:41:30.000Z",
+ "url": "https://intercom.help/nango/en/articles/9939221-test3",
+ "parent_id": null,
+ "parent_ids": [],
+ "parent_type": null
+ },
+ {
+ "type": "article",
+ "id": "9939210",
+ "workspace_id": "s0lour9i",
+ "title": "Untitled public article",
+ "description": "",
+ "body": "",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": "2024-09-30T14:39:42.000Z",
+ "updated_at": "2024-09-30T14:39:42.000Z",
+ "url": null,
+ "parent_id": null,
+ "parent_ids": [10521525],
+ "parent_type": null
+ },
+ {
+ "type": "article",
+ "id": "9939204",
+ "workspace_id": "s0lour9i",
+ "title": "Test 2",
+ "description": "This is a simple test",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": "2024-09-30T14:37:12.000Z",
+ "updated_at": "2024-09-30T14:38:02.000Z",
+ "url": "https://intercom.help/nango/en/articles/9939204-test-2",
+ "parent_id": null,
+ "parent_ids": [10521524],
+ "parent_type": null
+ },
+ {
+ "type": "article",
+ "id": "9932456",
+ "workspace_id": "s0lour9i",
+ "title": "Your first public article",
+ "description": "Some resources to help you understand how Articles can be used",
+ "body": "Public articles enable customers to self-serve and find support 24/7.
Teammates can also insert articles in Help Desk conversations, send them in Proactive Support messages, and use them to power the Fin AI Agent 💪
\n\nBuild a comprehensive knowledge base
\nOnce you've written your first few articles, you need to group them into collections to publish them on your Help Center (articles are only visible on your Help Center if they’re in a collection).
\n\nTo make your content easy to find, you need to:
\n\n- \n
Organize your collections into topics that matter most to your customers - this will make your Help Center easier to browse.
\n \n- \n
Add short descriptions to each collection to optimize them for search, and to help people better understand what they contain.
\n \n- \n
Customize your Help Center to match your brand or use Multi Help Center for various products/brands.
\n \n
Use Articles to power Fin AI Agent and Fin AI Copilot
\nFin can use your Intercom Articles to hold conversations and provide AI Answers. As your Articles get better, Fin AI Agent does too—along with its rate of automated resolution.
\n\n
For more tips on using public articles, visit our Help Center.
",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": "2024-09-28T11:54:01.000Z",
+ "updated_at": "2024-09-28T11:54:01.000Z",
+ "url": null,
+ "parent_id": 10521525,
+ "parent_ids": [10521525],
+ "parent_type": "collection"
+ }
+]
diff --git a/integrations/salesforce/mocks/accounts/batchDelete.json b/integrations/intercom/mocks/contacts/Contact/batchDelete.json
similarity index 100%
rename from integrations/salesforce/mocks/accounts/batchDelete.json
rename to integrations/intercom/mocks/contacts/Contact/batchDelete.json
diff --git a/integrations/intercom/mocks/contacts/Contact/batchSave.json b/integrations/intercom/mocks/contacts/Contact/batchSave.json
new file mode 100644
index 00000000..c1abaf88
--- /dev/null
+++ b/integrations/intercom/mocks/contacts/Contact/batchSave.json
@@ -0,0 +1,80 @@
+[
+ {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "workspace_id": "s0lour9i",
+ "external_id": "3892ec77-2638-4343-a883-54c1ead2e35e",
+ "type": "user",
+ "email": "example.user@projectmap.com",
+ "phone": null,
+ "name": "Example User",
+ "created_at": "2024-09-28T11:54:06.000Z",
+ "updated_at": "2024-09-28T11:54:25.000Z",
+ "last_seen_at": "2024-09-28T11:54:06.000Z",
+ "last_replied_at": "2024-09-28T11:54:23.000Z"
+ },
+ {
+ "id": "66fadfecea421c7b5057f6f0",
+ "workspace_id": "s0lour9i",
+ "external_id": "1234566",
+ "type": "user",
+ "email": "john.doe@example.com",
+ "phone": null,
+ "name": "John Doe",
+ "created_at": "2024-09-30T17:29:16.000Z",
+ "updated_at": "2024-09-30T17:29:16.000Z",
+ "last_seen_at": null,
+ "last_replied_at": null
+ },
+ {
+ "id": "66fae008c39645865b3c0443",
+ "workspace_id": "s0lour9i",
+ "external_id": "23477",
+ "type": "user",
+ "email": "james@test.com",
+ "phone": null,
+ "name": "James Test",
+ "created_at": "2024-09-30T17:29:44.000Z",
+ "updated_at": "2024-09-30T17:29:44.000Z",
+ "last_seen_at": null,
+ "last_replied_at": null
+ },
+ {
+ "id": "66fae01ac4f9bc7fb131a4a6",
+ "workspace_id": "s0lour9i",
+ "external_id": "894343",
+ "type": "user",
+ "email": "test@test.com",
+ "phone": null,
+ "name": null,
+ "created_at": "2024-09-30T17:30:02.000Z",
+ "updated_at": "2024-09-30T17:30:02.000Z",
+ "last_seen_at": null,
+ "last_replied_at": null
+ },
+ {
+ "id": "66fae0386198fdb3ae7e8746",
+ "workspace_id": "s0lour9i",
+ "external_id": "4353453",
+ "type": "user",
+ "email": "test@tes2t.com",
+ "phone": null,
+ "name": "Ian Test",
+ "created_at": "2024-09-30T17:30:33.000Z",
+ "updated_at": "2024-09-30T17:30:33.000Z",
+ "last_seen_at": null,
+ "last_replied_at": null
+ },
+ {
+ "id": "66fae05c6a8ee5104e5c6ee1",
+ "workspace_id": "s0lour9i",
+ "external_id": "324343",
+ "type": "user",
+ "email": "phil@inter.com",
+ "phone": null,
+ "name": "Phil Intercom",
+ "created_at": "2024-09-30T17:31:08.000Z",
+ "updated_at": "2024-09-30T17:31:08.000Z",
+ "last_seen_at": null,
+ "last_replied_at": null
+ }
+]
diff --git a/integrations/salesforce/mocks/articles/batchDelete.json b/integrations/intercom/mocks/conversations/Conversation/batchDelete.json
similarity index 100%
rename from integrations/salesforce/mocks/articles/batchDelete.json
rename to integrations/intercom/mocks/conversations/Conversation/batchDelete.json
diff --git a/integrations/intercom/mocks/conversations/Conversation/batchSave.json b/integrations/intercom/mocks/conversations/Conversation/batchSave.json
new file mode 100644
index 00000000..21b71162
--- /dev/null
+++ b/integrations/intercom/mocks/conversations/Conversation/batchSave.json
@@ -0,0 +1,117 @@
+[
+ {
+ "id": "7",
+ "created_at": "2024-09-30T23:13:05.000Z",
+ "updated_at": "2024-09-30T23:13:06.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": false,
+ "priority": "not_priority"
+ },
+ {
+ "id": "6",
+ "created_at": "2024-09-28T11:54:21.000Z",
+ "updated_at": "2024-09-28T11:54:24.000Z",
+ "waiting_since": "2024-09-28T11:54:23.000Z",
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": true,
+ "priority": "not_priority"
+ },
+ {
+ "id": "5",
+ "created_at": "2024-09-28T11:54:18.000Z",
+ "updated_at": "2024-09-28T11:54:21.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": true,
+ "priority": "not_priority"
+ },
+ {
+ "id": "3",
+ "created_at": "2024-09-28T11:54:12.000Z",
+ "updated_at": "2024-09-28T11:54:17.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": true,
+ "priority": "not_priority"
+ },
+ {
+ "id": "4",
+ "created_at": "2024-09-28T11:54:14.000Z",
+ "updated_at": "2024-09-28T11:54:17.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": false,
+ "priority": "not_priority"
+ },
+ {
+ "id": "1",
+ "created_at": "2024-09-28T11:54:06.000Z",
+ "updated_at": "2024-09-28T11:54:12.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [
+ {
+ "contact_id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ],
+ "state": "open",
+ "open": true,
+ "read": true,
+ "priority": "not_priority"
+ },
+ {
+ "id": "2",
+ "created_at": "2024-09-28T11:54:08.000Z",
+ "updated_at": "2024-09-28T11:54:12.000Z",
+ "waiting_since": null,
+ "snoozed_until": null,
+ "title": null,
+ "contacts": [],
+ "state": "open",
+ "open": true,
+ "read": false,
+ "priority": "not_priority"
+ }
+]
diff --git a/integrations/salesforce/mocks/contacts/batchDelete.json b/integrations/intercom/mocks/conversations/ConversationMessage/batchDelete.json
similarity index 100%
rename from integrations/salesforce/mocks/contacts/batchDelete.json
rename to integrations/intercom/mocks/conversations/ConversationMessage/batchDelete.json
diff --git a/integrations/intercom/mocks/conversations/ConversationMessage/batchSave.json b/integrations/intercom/mocks/conversations/ConversationMessage/batchSave.json
new file mode 100644
index 00000000..c62a1cab
--- /dev/null
+++ b/integrations/intercom/mocks/conversations/ConversationMessage/batchSave.json
@@ -0,0 +1,288 @@
+[
+ {
+ "id": "125299165",
+ "conversation_id": "7",
+ "body": "This is a test example",
+ "type": "comment",
+ "created_at": "2024-09-30T23:13:05.000Z",
+ "updated_at": "2024-09-30T23:13:06.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "6",
+ "conversation_id": "6",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:21.000Z",
+ "updated_at": "2024-09-28T11:54:24.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "32",
+ "conversation_id": "6",
+ "body": "ℹ️ This is an example of a simple conversation. Explore your inbox to check out more examples and see how more complex conversations work. When you’re ready to set up the rest of your help desk, head back over to the getting started guide.",
+ "type": "note",
+ "created_at": "2024-09-28T11:54:22.000Z",
+ "updated_at": "2024-09-28T11:54:22.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "33",
+ "conversation_id": "6",
+ "body": "Hi, I just created a project and I want to add a team member to it. How do I do that?",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:22.000Z",
+ "updated_at": "2024-09-28T11:54:22.000Z",
+ "author": {
+ "type": "user",
+ "name": "Example User",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ },
+ {
+ "id": "34",
+ "conversation_id": "6",
+ "body": "Hello! Simply go to the project dashboard, click on the ‘Team’ tab, and then select ‘Add Member’. You can enter their email address to send an invite. Have you found the option?",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:23.000Z",
+ "updated_at": "2024-09-28T11:54:23.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "35",
+ "conversation_id": "6",
+ "body": "Yes, I see it now. I’ve added my colleague. Thank you!",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:23.000Z",
+ "updated_at": "2024-09-28T11:54:23.000Z",
+ "author": {
+ "type": "user",
+ "name": "Example User",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ },
+ {
+ "id": "5",
+ "conversation_id": "5",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:18.000Z",
+ "updated_at": "2024-09-28T11:54:21.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "24",
+ "conversation_id": "5",
+ "body": "[Image \"customer_ticket_example.png?expires=1727777700&signature=71b5e65d434e685a661705b5ebab52302788ea9096cf7b5b4cd43c49880b42b2&req=fSAlF8h8n4BYFb4f3HP0gIoXin1VzYIcWiFf7hD8nErIB6w%2BF0%2BkzjORPRPP%0A6oZFukzKaTfte%2FF9yg%3D%3D%0A\"] ℹ️ This is an example of a customer ticket. Customer tickets help you solve complex issues without losing context. Use them to collect the info you need up-front, update your customers in real-time, and track progress.",
+ "type": "note",
+ "created_at": "2024-09-28T11:54:18.000Z",
+ "updated_at": "2024-09-28T11:54:18.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "25",
+ "conversation_id": "5",
+ "body": "Hello, I'm having a problem with ProjectMap. When I try to access my project files, it says \"Permission Denied.\" I used to have access, but it suddenly stopped working yesterday.",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:19.000Z",
+ "updated_at": "2024-09-28T11:54:19.000Z",
+ "author": {
+ "type": "user",
+ "name": "Example User",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ },
+ {
+ "id": "26",
+ "conversation_id": "5",
+ "body": "Hi there, I'm sorry to hear about the issue you're facing. I’ll need some time to investigate this, so I’ll create a support ticket for you and get back to you as soon as I have an answer.",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:19.000Z",
+ "updated_at": "2024-09-28T11:54:19.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "3",
+ "conversation_id": "3",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:12.000Z",
+ "updated_at": "2024-09-28T11:54:17.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "14",
+ "conversation_id": "3",
+ "body": "ℹ️ This is an example of a conversation with a linked Back-office ticket. Use back-office tickets to assign a separate ticket to your back-office teams and collaborate in private with internal notes.",
+ "type": "note",
+ "created_at": "2024-09-28T11:54:13.000Z",
+ "updated_at": "2024-09-28T11:54:13.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "15",
+ "conversation_id": "3",
+ "body": "Hello, I've encountered a problem with my recent bill. There's a charge for a user license that I never ordered or authorized. The invoice number is #12345. Can you assist me with this issue? Kind regards, David",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:13.000Z",
+ "updated_at": "2024-09-28T11:54:13.000Z",
+ "author": {
+ "type": "user",
+ "name": "Example User",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ },
+ {
+ "id": "16",
+ "conversation_id": "3",
+ "body": "Hi, there! I'm sorry to hear about the issue you're facing. I'd be happy to help you with this. I’ll get in touch with our finance team to find out why you’ve been charged and get back to you. Best, Gustavs",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:14.000Z",
+ "updated_at": "2024-09-28T11:54:14.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "4",
+ "conversation_id": "4",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:14.000Z",
+ "updated_at": "2024-09-28T11:54:17.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "21",
+ "conversation_id": "4",
+ "body": "Could you please take a look at why this customer was charged and get back to me?",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:17.000Z",
+ "updated_at": "2024-09-28T11:54:17.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "1",
+ "conversation_id": "1",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:06.000Z",
+ "updated_at": "2024-09-28T11:54:12.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "2",
+ "conversation_id": "1",
+ "body": "ℹ️ This is an example of a conversation with a linked Tracker ticket. Use tracker tickets to manage all conversations related to a widespread issue with a single ticket.",
+ "type": "note",
+ "created_at": "2024-09-28T11:54:07.000Z",
+ "updated_at": "2024-09-28T11:54:07.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "3",
+ "conversation_id": "1",
+ "body": "Hello, I'm having a problem with the chart export feature. The exported PDF is missing crucial project milestones and isn't displaying some tasks correctly. What should I do?",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:08.000Z",
+ "updated_at": "2024-09-28T11:54:08.000Z",
+ "author": {
+ "type": "user",
+ "name": "Example User",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ },
+ {
+ "id": "4",
+ "conversation_id": "1",
+ "body": "Hi there! Thank you for the information. We'll create a ticket for this export issue and notify you once it's resolved. Please stay tuned for updates.",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:08.000Z",
+ "updated_at": "2024-09-28T11:54:08.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ },
+ {
+ "id": "2",
+ "conversation_id": "2",
+ "body": "",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:08.000Z",
+ "updated_at": "2024-09-28T11:54:12.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Operator",
+ "id": "7803409"
+ }
+ },
+ {
+ "id": "11",
+ "conversation_id": "2",
+ "body": "Looks like we have a bug with the PDF export feature. Please take a look and get back to me.",
+ "type": "comment",
+ "created_at": "2024-09-28T11:54:12.000Z",
+ "updated_at": "2024-09-28T11:54:12.000Z",
+ "author": {
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "id": "7803408"
+ }
+ }
+]
diff --git a/integrations/intercom/mocks/nango/get/proxy/articles/articles.json b/integrations/intercom/mocks/nango/get/proxy/articles/articles.json
new file mode 100644
index 00000000..6c358303
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/articles/articles.json
@@ -0,0 +1,109 @@
+{
+ "type": "list",
+ "pages": {
+ "type": "pages",
+ "next": "https://api.intercom.io/articles?per_page=100&page=1",
+ "page": 0,
+ "per_page": 100,
+ "total_pages": 1
+ },
+ "total_count": 6,
+ "data": [
+ {
+ "id": "9939230",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Lorem text",
+ "description": "More Test",
+ "body": "\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707372,
+ "updated_at": 1727707424,
+ "url": "https://intercom.help/nango/en/articles/9939230-lorem-text"
+ },
+ {
+ "id": "9939225",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Lorem",
+ "description": "Another lorem",
+ "body": "Blorf quaxle fimpedoo, snerk whizzleplop! Glibber wompus frindle blargh, zindle skroop. Jibber jabber wibbly wobbly, froodlesnorp quibbly floop. Squeegee munchkin, vleep fubbit, and wooshy flangle bork. Doodlewhap quarnish, yorp splinkledorf, flumple wizzle. Higgledy piggledy, snorpwobble greeble plonkit.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707316,
+ "updated_at": 1727707363,
+ "url": "https://intercom.help/nango/en/articles/9939225-lorem"
+ },
+ {
+ "id": "9939221",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [],
+ "title": "Test3",
+ "description": "Lorem ipsum dolor",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707257,
+ "updated_at": 1727707290,
+ "url": "https://intercom.help/nango/en/articles/9939221-test3"
+ },
+ {
+ "id": "9939210",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521525],
+ "title": "Untitled public article",
+ "description": "",
+ "body": "",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": 1727707182,
+ "updated_at": 1727707182,
+ "url": null
+ },
+ {
+ "id": "9939204",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Test 2",
+ "description": "This is a simple test",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707032,
+ "updated_at": 1727707082,
+ "url": "https://intercom.help/nango/en/articles/9939204-test-2"
+ },
+ {
+ "id": "9932456",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": 10521525,
+ "parent_type": "collection",
+ "parent_ids": [10521525],
+ "title": "Your first public article",
+ "description": "Some resources to help you understand how Articles can be used",
+ "body": "Public articles enable customers to self-serve and find support 24/7.
Teammates can also insert articles in Help Desk conversations, send them in Proactive Support messages, and use them to power the Fin AI Agent 💪
\n\nBuild a comprehensive knowledge base
\nOnce you've written your first few articles, you need to group them into collections to publish them on your Help Center (articles are only visible on your Help Center if they’re in a collection).
\n\nTo make your content easy to find, you need to:
\n\n- \n
Organize your collections into topics that matter most to your customers - this will make your Help Center easier to browse.
\n \n- \n
Add short descriptions to each collection to optimize them for search, and to help people better understand what they contain.
\n \n- \n
Customize your Help Center to match your brand or use Multi Help Center for various products/brands.
\n \n
Use Articles to power Fin AI Agent and Fin AI Copilot
\nFin can use your Intercom Articles to hold conversations and provide AI Answers. As your Articles get better, Fin AI Agent does too—along with its rate of automated resolution.
\n\n
For more tips on using public articles, visit our Help Center.
",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": 1727524441,
+ "updated_at": 1727524441,
+ "url": null
+ }
+ ]
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/articles/contacts.json b/integrations/intercom/mocks/nango/get/proxy/articles/contacts.json
new file mode 100644
index 00000000..732cb267
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/articles/contacts.json
@@ -0,0 +1,108 @@
+{
+ "type": "list",
+ "pages": {
+ "type": "pages",
+ "page": 1,
+ "per_page": 100,
+ "total_pages": 1
+ },
+ "total_count": 6,
+ "data": [
+ {
+ "id": "9939230",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Lorem text",
+ "description": "More Test",
+ "body": "\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707372,
+ "updated_at": 1727707424,
+ "url": "https://intercom.help/nango/en/articles/9939230-lorem-text"
+ },
+ {
+ "id": "9939225",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Lorem",
+ "description": "Another lorem",
+ "body": "Blorf quaxle fimpedoo, snerk whizzleplop! Glibber wompus frindle blargh, zindle skroop. Jibber jabber wibbly wobbly, froodlesnorp quibbly floop. Squeegee munchkin, vleep fubbit, and wooshy flangle bork. Doodlewhap quarnish, yorp splinkledorf, flumple wizzle. Higgledy piggledy, snorpwobble greeble plonkit.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707316,
+ "updated_at": 1727707363,
+ "url": "https://intercom.help/nango/en/articles/9939225-lorem"
+ },
+ {
+ "id": "9939221",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [],
+ "title": "Test3",
+ "description": "Lorem ipsum dolor",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707257,
+ "updated_at": 1727707290,
+ "url": "https://intercom.help/nango/en/articles/9939221-test3"
+ },
+ {
+ "id": "9939210",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521525],
+ "title": "Untitled public article",
+ "description": "",
+ "body": "",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": 1727707182,
+ "updated_at": 1727707182,
+ "url": null
+ },
+ {
+ "id": "9939204",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": null,
+ "parent_type": null,
+ "parent_ids": [10521524],
+ "title": "Test 2",
+ "description": "This is a simple test",
+ "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\n",
+ "author_id": 7803408,
+ "state": "published",
+ "created_at": 1727707032,
+ "updated_at": 1727707082,
+ "url": "https://intercom.help/nango/en/articles/9939204-test-2"
+ },
+ {
+ "id": "9932456",
+ "type": "article",
+ "workspace_id": "s0lour9i",
+ "parent_id": 10521525,
+ "parent_type": "collection",
+ "parent_ids": [10521525],
+ "title": "Your first public article",
+ "description": "Some resources to help you understand how Articles can be used",
+ "body": "Public articles enable customers to self-serve and find support 24/7.
Teammates can also insert articles in Help Desk conversations, send them in Proactive Support messages, and use them to power the Fin AI Agent 💪
\n\nBuild a comprehensive knowledge base
\nOnce you've written your first few articles, you need to group them into collections to publish them on your Help Center (articles are only visible on your Help Center if they’re in a collection).
\n\nTo make your content easy to find, you need to:
\n\n- \n
Organize your collections into topics that matter most to your customers - this will make your Help Center easier to browse.
\n \n- \n
Add short descriptions to each collection to optimize them for search, and to help people better understand what they contain.
\n \n- \n
Customize your Help Center to match your brand or use Multi Help Center for various products/brands.
\n \n
Use Articles to power Fin AI Agent and Fin AI Copilot
\nFin can use your Intercom Articles to hold conversations and provide AI Answers. As your Articles get better, Fin AI Agent does too—along with its rate of automated resolution.
\n\n
For more tips on using public articles, visit our Help Center.
",
+ "author_id": 7803408,
+ "state": "draft",
+ "created_at": 1727524441,
+ "updated_at": 1727524441,
+ "url": null
+ }
+ ]
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/contacts/contacts.json b/integrations/intercom/mocks/nango/get/proxy/contacts/contacts.json
new file mode 100644
index 00000000..5e2732a7
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/contacts/contacts.json
@@ -0,0 +1,594 @@
+{
+ "type": "list",
+ "data": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "workspace_id": "s0lour9i",
+ "external_id": "3892ec77-2638-4343-a883-54c1ead2e35e",
+ "role": "user",
+ "email": "example.user@projectmap.com",
+ "phone": null,
+ "name": "Example User",
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727524446,
+ "updated_at": 1727524465,
+ "signed_up_at": 1727524446,
+ "last_seen_at": 1727524446,
+ "last_replied_at": 1727524463,
+ "last_contacted_at": 1727524463,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": "Ireland",
+ "region": "Leinster",
+ "city": "Dublin",
+ "country_code": "IRL",
+ "continent_code": "EU"
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66f7ee5ea9e2c41564b4c362/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66f7ee5ea9e2c41564b4c362/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [
+ {
+ "id": "66f7ee5ea9e2c41564b4c363",
+ "type": "company",
+ "url": "/companies/66f7ee5ea9e2c41564b4c363"
+ }
+ ],
+ "url": "/contacts/66f7ee5ea9e2c41564b4c362/companies",
+ "total_count": 1,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66f7ee5ea9e2c41564b4c362/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66f7ee5ea9e2c41564b4c362/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ },
+ {
+ "type": "contact",
+ "id": "66fadfecea421c7b5057f6f0",
+ "workspace_id": "s0lour9i",
+ "external_id": "1234566",
+ "role": "user",
+ "email": "john.doe@example.com",
+ "phone": null,
+ "name": "John Doe",
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727717356,
+ "updated_at": 1727717356,
+ "signed_up_at": null,
+ "last_seen_at": null,
+ "last_replied_at": null,
+ "last_contacted_at": null,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": null,
+ "region": null,
+ "city": null,
+ "country_code": null,
+ "continent_code": null
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fadfecea421c7b5057f6f0/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fadfecea421c7b5057f6f0/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fadfecea421c7b5057f6f0/companies",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fadfecea421c7b5057f6f0/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fadfecea421c7b5057f6f0/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ },
+ {
+ "type": "contact",
+ "id": "66fae008c39645865b3c0443",
+ "workspace_id": "s0lour9i",
+ "external_id": "23477",
+ "role": "user",
+ "email": "james@test.com",
+ "phone": null,
+ "name": "James Test",
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727717384,
+ "updated_at": 1727717384,
+ "signed_up_at": null,
+ "last_seen_at": null,
+ "last_replied_at": null,
+ "last_contacted_at": null,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": null,
+ "region": null,
+ "city": null,
+ "country_code": null,
+ "continent_code": null
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae008c39645865b3c0443/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae008c39645865b3c0443/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae008c39645865b3c0443/companies",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae008c39645865b3c0443/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae008c39645865b3c0443/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ },
+ {
+ "type": "contact",
+ "id": "66fae01ac4f9bc7fb131a4a6",
+ "workspace_id": "s0lour9i",
+ "external_id": "894343",
+ "role": "user",
+ "email": "test@test.com",
+ "phone": null,
+ "name": null,
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727717402,
+ "updated_at": 1727717402,
+ "signed_up_at": null,
+ "last_seen_at": null,
+ "last_replied_at": null,
+ "last_contacted_at": null,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": null,
+ "region": null,
+ "city": null,
+ "country_code": null,
+ "continent_code": null
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae01ac4f9bc7fb131a4a6/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae01ac4f9bc7fb131a4a6/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae01ac4f9bc7fb131a4a6/companies",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae01ac4f9bc7fb131a4a6/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae01ac4f9bc7fb131a4a6/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ },
+ {
+ "type": "contact",
+ "id": "66fae0386198fdb3ae7e8746",
+ "workspace_id": "s0lour9i",
+ "external_id": "4353453",
+ "role": "user",
+ "email": "test@tes2t.com",
+ "phone": null,
+ "name": "Ian Test",
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727717433,
+ "updated_at": 1727717433,
+ "signed_up_at": null,
+ "last_seen_at": null,
+ "last_replied_at": null,
+ "last_contacted_at": null,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": null,
+ "region": null,
+ "city": null,
+ "country_code": null,
+ "continent_code": null
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae0386198fdb3ae7e8746/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae0386198fdb3ae7e8746/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae0386198fdb3ae7e8746/companies",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae0386198fdb3ae7e8746/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae0386198fdb3ae7e8746/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ },
+ {
+ "type": "contact",
+ "id": "66fae05c6a8ee5104e5c6ee1",
+ "workspace_id": "s0lour9i",
+ "external_id": "324343",
+ "role": "user",
+ "email": "phil@inter.com",
+ "phone": null,
+ "name": "Phil Intercom",
+ "avatar": null,
+ "owner_id": null,
+ "social_profiles": {
+ "type": "list",
+ "data": []
+ },
+ "has_hard_bounced": false,
+ "marked_email_as_spam": false,
+ "unsubscribed_from_emails": false,
+ "created_at": 1727717468,
+ "updated_at": 1727717468,
+ "signed_up_at": null,
+ "last_seen_at": null,
+ "last_replied_at": null,
+ "last_contacted_at": null,
+ "last_email_opened_at": null,
+ "last_email_clicked_at": null,
+ "language_override": null,
+ "browser": null,
+ "browser_version": null,
+ "browser_language": null,
+ "os": null,
+ "location": {
+ "type": "location",
+ "country": null,
+ "region": null,
+ "city": null,
+ "country_code": null,
+ "continent_code": null
+ },
+ "android_app_name": null,
+ "android_app_version": null,
+ "android_device": null,
+ "android_os_version": null,
+ "android_sdk_version": null,
+ "android_last_seen_at": null,
+ "ios_app_name": null,
+ "ios_app_version": null,
+ "ios_device": null,
+ "ios_os_version": null,
+ "ios_sdk_version": null,
+ "ios_last_seen_at": null,
+ "custom_attributes": {},
+ "tags": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae05c6a8ee5104e5c6ee1/tags",
+ "total_count": 0,
+ "has_more": false
+ },
+ "notes": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae05c6a8ee5104e5c6ee1/notes",
+ "total_count": 0,
+ "has_more": false
+ },
+ "companies": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae05c6a8ee5104e5c6ee1/companies",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_out_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae05c6a8ee5104e5c6ee1/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "opted_in_subscription_types": {
+ "type": "list",
+ "data": [],
+ "url": "/contacts/66fae05c6a8ee5104e5c6ee1/subscriptions",
+ "total_count": 0,
+ "has_more": false
+ },
+ "utm_campaign": null,
+ "utm_content": null,
+ "utm_medium": null,
+ "utm_source": null,
+ "utm_term": null,
+ "referrer": null,
+ "sms_consent": false,
+ "unsubscribed_from_sms": false
+ }
+ ],
+ "total_count": 6,
+ "pages": {
+ "type": "pages",
+ "page": 1,
+ "per_page": 100,
+ "total_pages": 1
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/1/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/1/conversations.json
new file mode 100644
index 00000000..ba351b80
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/1/conversations.json
@@ -0,0 +1,251 @@
+{
+ "type": "conversation",
+ "id": "1",
+ "created_at": 1727524446,
+ "updated_at": 1727524452,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "1",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524448,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524448,
+ "first_assignment_at": 1727524452,
+ "first_admin_reply_at": 1727524448,
+ "first_close_at": null,
+ "last_assignment_at": 1727524452,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524448,
+ "last_admin_reply_at": 1727524448,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 9
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null,
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "1",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524447,
+ "updated_at": 1727524447,
+ "notified_at": 1727524447,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "2",
+ "part_type": "note",
+ "body": "ℹ️ This is an example of a conversation with a linked Tracker ticket. Use tracker tickets to manage all conversations related to a widespread issue with a single ticket.",
+ "created_at": 1727524447,
+ "updated_at": 1727524447,
+ "notified_at": 1727524447,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "3",
+ "part_type": "comment",
+ "body": "Hello, I'm having a problem with the chart export feature. The exported PDF is missing crucial project milestones and isn't displaying some tasks correctly. What should I do?",
+ "created_at": 1727524448,
+ "updated_at": 1727524448,
+ "notified_at": 1727524448,
+ "assigned_to": null,
+ "author": {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "type": "user",
+ "name": "Example User",
+ "email": "example.user@projectmap.com"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "4",
+ "part_type": "comment",
+ "body": "Hi there! Thank you for the information. We'll create a ticket for this export issue and notify you once it's resolved. Please stay tuned for updates.",
+ "created_at": 1727524448,
+ "updated_at": 1727524448,
+ "notified_at": 1727524448,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "6",
+ "part_type": "conversation_attribute_updated_by_admin",
+ "body": null,
+ "created_at": 1727524449,
+ "updated_at": 1727524449,
+ "notified_at": 1727524449,
+ "assigned_to": null,
+ "author": {
+ "id": "7803409",
+ "type": "bot",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "8",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524450,
+ "updated_at": 1727524450,
+ "notified_at": 1727524450,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "10",
+ "part_type": "entity_linked",
+ "body": null,
+ "created_at": 1727524452,
+ "updated_at": 1727524452,
+ "notified_at": 1727524452,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "12",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524452,
+ "updated_at": 1727524452,
+ "notified_at": 1727524452,
+ "assigned_to": {
+ "type": "team",
+ "id": "7803411"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 8
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/2/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/2/conversations.json
new file mode 100644
index 00000000..1daf5deb
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/2/conversations.json
@@ -0,0 +1,206 @@
+{
+ "type": "conversation",
+ "id": "2",
+ "created_at": 1727524448,
+ "updated_at": 1727524452,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "2",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": []
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 5
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Ticket category": "Tracker ticket",
+ "_default_title_": "Example: PDF export broken",
+ "_default_description_": "ℹ️ This is an example of a Tracker ticket.
Tracker tickets let you manage all conversations related to a widespread issue with a single ticket. Use them to get all teams working from the same ticket and share updates to all impacted customers at the same time.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Chart export feature malfunctioning - exported PDFs are omitting key project milestones and misrepresenting some tasks.",
+ "Customer reports": 1,
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 2,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/2",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: PDF export broken",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "ℹ️ This is an example of a Tracker ticket.
Tracker tickets let you manage all conversations related to a widespread issue with a single ticket. Use them to get all teams working from the same ticket and share updates to all impacted customers at the same time.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Chart export feature malfunctioning - exported PDFs are omitting key project milestones and misrepresenting some tasks.",
+ "type": "string"
+ },
+ "Order number": {
+ "value": null,
+ "type": "string"
+ },
+ "Amount": {
+ "value": null,
+ "type": "string"
+ },
+ "Reason": {
+ "value": null,
+ "type": "string"
+ },
+ "Supporting evidence": {
+ "value": null,
+ "type": "files"
+ }
+ },
+ "state": "submitted",
+ "ticket_type": "Bug report",
+ "ticket_type_description": "Capture bug reports that impacts multiple customers. All relevant customer conversations will be linked to a Tracker ticket to act as the single source of truth.",
+ "ticket_type_emoji": "⚠️",
+ "ticket_custom_state_admin_label": "Submitted",
+ "ticket_custom_state_user_label": "Submitted"
+ },
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "5",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524449,
+ "updated_at": 1727524449,
+ "notified_at": 1727524449,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "7",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524450,
+ "updated_at": 1727524450,
+ "notified_at": 1727524450,
+ "assigned_to": {
+ "type": "team",
+ "id": "7803412"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "9",
+ "part_type": "entity_linked",
+ "body": null,
+ "created_at": 1727524451,
+ "updated_at": 1727524451,
+ "notified_at": 1727524451,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "11",
+ "part_type": "assignment",
+ "body": "Looks like we have a bug with the PDF export feature. Please take a look and get back to me.",
+ "created_at": 1727524452,
+ "updated_at": 1727524452,
+ "notified_at": 1727524452,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 4
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/3/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/3/conversations.json
new file mode 100644
index 00000000..766d3bf0
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/3/conversations.json
@@ -0,0 +1,232 @@
+{
+ "type": "conversation",
+ "id": "3",
+ "created_at": 1727524452,
+ "updated_at": 1727524457,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "3",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524453,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524453,
+ "first_assignment_at": 1727524457,
+ "first_admin_reply_at": 1727524454,
+ "first_close_at": null,
+ "last_assignment_at": 1727524457,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524453,
+ "last_admin_reply_at": 1727524454,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 8
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null,
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "13",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524453,
+ "updated_at": 1727524453,
+ "notified_at": 1727524453,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "14",
+ "part_type": "note",
+ "body": "ℹ️ This is an example of a conversation with a linked Back-office ticket. Use back-office tickets to assign a separate ticket to your back-office teams and collaborate in private with internal notes.",
+ "created_at": 1727524453,
+ "updated_at": 1727524453,
+ "notified_at": 1727524453,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "15",
+ "part_type": "comment",
+ "body": "Hello, I've encountered a problem with my recent bill. There's a charge for a user license that I never ordered or authorized. The invoice number is #12345. Can you assist me with this issue? Kind regards, David",
+ "created_at": 1727524453,
+ "updated_at": 1727524453,
+ "notified_at": 1727524453,
+ "assigned_to": null,
+ "author": {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "type": "user",
+ "name": "Example User",
+ "email": "example.user@projectmap.com"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "16",
+ "part_type": "comment",
+ "body": "Hi, there! I'm sorry to hear about the issue you're facing. I'd be happy to help you with this. I’ll get in touch with our finance team to find out why you’ve been charged and get back to you. Best, Gustavs",
+ "created_at": 1727524454,
+ "updated_at": 1727524454,
+ "notified_at": 1727524454,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "18",
+ "part_type": "conversation_attribute_updated_by_admin",
+ "body": null,
+ "created_at": 1727524455,
+ "updated_at": 1727524455,
+ "notified_at": 1727524455,
+ "assigned_to": null,
+ "author": {
+ "id": "7803409",
+ "type": "bot",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "20",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524455,
+ "updated_at": 1727524455,
+ "notified_at": 1727524455,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "22",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524457,
+ "updated_at": 1727524457,
+ "notified_at": 1727524457,
+ "assigned_to": {
+ "type": "team",
+ "id": "7803411"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 7
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/4/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/4/conversations.json
new file mode 100644
index 00000000..b23bb6da
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/4/conversations.json
@@ -0,0 +1,184 @@
+{
+ "type": "conversation",
+ "id": "4",
+ "created_at": 1727524454,
+ "updated_at": 1727524457,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "4",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 4
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Ticket category": "Back-office ticket",
+ "_default_title_": "Example: Unauthorized user license charge",
+ "_default_description_": "ℹ️ This is an example of a Back-office ticket.
A back-office ticket can be assigned separately to a back-office team, letting you add your own internal notes. Use them to keep conversations and customer data accessible, so your teams never lose context.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Customer reports an unauthorized user license charge on invoice #12345. The user account is \"User123\".",
+ "Priority": "High (needed in 24 hours)",
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 4,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/4",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: Unauthorized user license charge",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "ℹ️ This is an example of a Back-office ticket.
A back-office ticket can be assigned separately to a back-office team, letting you add your own internal notes. Use them to keep conversations and customer data accessible, so your teams never lose context.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Customer reports an unauthorized user license charge on invoice #12345. The user account is \"User123\".",
+ "type": "string"
+ },
+ "Feature": {
+ "value": null,
+ "type": "string"
+ },
+ "Priority": {
+ "value": "High (needed in 24 hours)",
+ "type": "string"
+ }
+ },
+ "state": "submitted",
+ "ticket_type": "Internal task",
+ "ticket_type_description": "When the support agent need to collaborate with a back-office team to resolve a customer request, create a Back-office internal ticket and assign it to the relevant team.",
+ "ticket_type_emoji": "✅",
+ "ticket_custom_state_admin_label": "Submitted",
+ "ticket_custom_state_user_label": "Submitted"
+ },
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "17",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524455,
+ "updated_at": 1727524455,
+ "notified_at": 1727524455,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "19",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524455,
+ "updated_at": 1727524455,
+ "notified_at": 1727524455,
+ "assigned_to": {
+ "type": "team",
+ "id": "7803412"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "21",
+ "part_type": "assignment",
+ "body": "Could you please take a look at why this customer was charged and get back to me?",
+ "created_at": 1727524457,
+ "updated_at": 1727524457,
+ "notified_at": 1727524457,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 3
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/5/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/5/conversations.json
new file mode 100644
index 00000000..51190392
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/5/conversations.json
@@ -0,0 +1,283 @@
+{
+ "type": "conversation",
+ "id": "5",
+ "created_at": 1727524458,
+ "updated_at": 1727524461,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "5",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524459,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524459,
+ "first_assignment_at": 1727524460,
+ "first_admin_reply_at": 1727524459,
+ "first_close_at": null,
+ "last_assignment_at": 1727524460,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524459,
+ "last_admin_reply_at": 1727524459,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 9
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Created by": 7803408,
+ "Ticket category": "Customer ticket",
+ "_default_title_": "Example: Access issue",
+ "_default_description_": "User reports a sudden 'Permission Denied' error while accessing project files, which was previously accessible.",
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 5,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/5",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: Access issue",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "User reports a sudden 'Permission Denied' error while accessing project files, which was previously accessible.",
+ "type": "string"
+ },
+ "Platforms": {
+ "value": null,
+ "type": "string"
+ },
+ "Root cause": {
+ "value": null,
+ "type": "string"
+ }
+ },
+ "state": "in_progress",
+ "ticket_type": "Support Request",
+ "ticket_type_description": "When a customer query can’t be instantly resolved, convert to support request ticket to ensure the right details are captured and allow customers to keep track of progress.",
+ "ticket_type_emoji": "🎟",
+ "ticket_custom_state_admin_label": "In progress",
+ "ticket_custom_state_user_label": "In progress"
+ },
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "23",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524458,
+ "updated_at": 1727524458,
+ "notified_at": 1727524458,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "24",
+ "part_type": "note",
+ "body": "[Image \"customer_ticket_example.png?expires=1727777700&signature=71b5e65d434e685a661705b5ebab52302788ea9096cf7b5b4cd43c49880b42b2&req=fSAlF8h8n4BYFb4f3HP0gIoXin1VzYIcWiFf7hD8nErIB6w%2BF0%2BkzjORPRPP%0A6oZFukzKaTfte%2FF9yg%3D%3D%0A\"] ℹ️ This is an example of a customer ticket. Customer tickets help you solve complex issues without losing context. Use them to collect the info you need up-front, update your customers in real-time, and track progress.",
+ "created_at": 1727524458,
+ "updated_at": 1727524458,
+ "notified_at": 1727524458,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "25",
+ "part_type": "comment",
+ "body": "Hello, I'm having a problem with ProjectMap. When I try to access my project files, it says \"Permission Denied.\" I used to have access, but it suddenly stopped working yesterday.",
+ "created_at": 1727524459,
+ "updated_at": 1727524459,
+ "notified_at": 1727524459,
+ "assigned_to": null,
+ "author": {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "type": "user",
+ "name": "Example User",
+ "email": "example.user@projectmap.com"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "26",
+ "part_type": "comment",
+ "body": "Hi there, I'm sorry to hear about the issue you're facing. I’ll need some time to investigate this, so I’ll create a support ticket for you and get back to you as soon as I have an answer.",
+ "created_at": 1727524459,
+ "updated_at": 1727524459,
+ "notified_at": 1727524459,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "27",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524460,
+ "updated_at": 1727524460,
+ "notified_at": 1727524460,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "28",
+ "part_type": "conversation_attribute_updated_by_admin",
+ "body": null,
+ "created_at": 1727524460,
+ "updated_at": 1727524460,
+ "notified_at": 1727524460,
+ "assigned_to": null,
+ "author": {
+ "id": "7803409",
+ "type": "bot",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "29",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524460,
+ "updated_at": 1727524460,
+ "notified_at": 1727524460,
+ "assigned_to": {
+ "type": "team",
+ "id": "7803412"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "30",
+ "part_type": "ticket_state_updated_by_admin",
+ "body": null,
+ "created_at": 1727524461,
+ "updated_at": 1727524461,
+ "notified_at": 1727524461,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 8
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/6/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/6/conversations.json
new file mode 100644
index 00000000..ef1725fc
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/6/conversations.json
@@ -0,0 +1,210 @@
+{
+ "type": "conversation",
+ "id": "6",
+ "created_at": 1727524461,
+ "updated_at": 1727524464,
+ "waiting_since": 1727524463,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "6",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524462,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524462,
+ "first_assignment_at": null,
+ "first_admin_reply_at": 1727524463,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524463,
+ "last_admin_reply_at": 1727524463,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 7
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null,
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "31",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727524462,
+ "updated_at": 1727524462,
+ "notified_at": 1727524462,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "32",
+ "part_type": "note",
+ "body": "ℹ️ This is an example of a simple conversation. Explore your inbox to check out more examples and see how more complex conversations work. When you’re ready to set up the rest of your help desk, head back over to the getting started guide.",
+ "created_at": 1727524462,
+ "updated_at": 1727524462,
+ "notified_at": 1727524462,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "33",
+ "part_type": "comment",
+ "body": "Hi, I just created a project and I want to add a team member to it. How do I do that?",
+ "created_at": 1727524462,
+ "updated_at": 1727524462,
+ "notified_at": 1727524462,
+ "assigned_to": null,
+ "author": {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "type": "user",
+ "name": "Example User",
+ "email": "example.user@projectmap.com"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "34",
+ "part_type": "comment",
+ "body": "Hello! Simply go to the project dashboard, click on the ‘Team’ tab, and then select ‘Add Member’. You can enter their email address to send an invite. Have you found the option?",
+ "created_at": 1727524463,
+ "updated_at": 1727524463,
+ "notified_at": 1727524463,
+ "assigned_to": null,
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "35",
+ "part_type": "comment",
+ "body": "Yes, I see it now. I’ve added my colleague. Thank you!",
+ "created_at": 1727524463,
+ "updated_at": 1727524463,
+ "notified_at": 1727524463,
+ "assigned_to": null,
+ "author": {
+ "id": "66f7ee5ea9e2c41564b4c362",
+ "type": "user",
+ "name": "Example User",
+ "email": "example.user@projectmap.com"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "36",
+ "part_type": "conversation_attribute_updated_by_admin",
+ "body": null,
+ "created_at": 1727524464,
+ "updated_at": 1727524464,
+ "notified_at": 1727524464,
+ "assigned_to": null,
+ "author": {
+ "id": "7803409",
+ "type": "bot",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 6
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/7/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/7/conversations.json
new file mode 100644
index 00000000..49bcb9aa
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/7/conversations.json
@@ -0,0 +1,130 @@
+{
+ "type": "conversation",
+ "id": "7",
+ "created_at": 1727737985,
+ "updated_at": 1727737986,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": "conversation",
+ "id": "125299165",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "This is a test example",
+ "author": {
+ "type": "admin",
+ "id": "7803408",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": null,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 3
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": null,
+ "conversation_parts": {
+ "type": "conversation_part.list",
+ "conversation_parts": [
+ {
+ "type": "conversation_part",
+ "id": "37",
+ "part_type": "language_detection_details",
+ "body": null,
+ "created_at": 1727737986,
+ "updated_at": 1727737986,
+ "notified_at": 1727737986,
+ "assigned_to": null,
+ "author": {
+ "id": "7803409",
+ "type": "bot",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ },
+ {
+ "type": "conversation_part",
+ "id": "38",
+ "part_type": "assignment",
+ "body": null,
+ "created_at": 1727737986,
+ "updated_at": 1727737986,
+ "notified_at": 1727737986,
+ "assigned_to": {
+ "type": "admin",
+ "id": "7803408"
+ },
+ "author": {
+ "id": "7803408",
+ "type": "admin",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "external_id": null,
+ "redacted": false
+ }
+ ],
+ "total_count": 2
+ }
+}
diff --git a/integrations/intercom/mocks/nango/get/proxy/conversations/conversations.json b/integrations/intercom/mocks/nango/get/proxy/conversations/conversations.json
new file mode 100644
index 00000000..ea95ac8b
--- /dev/null
+++ b/integrations/intercom/mocks/nango/get/proxy/conversations/conversations.json
@@ -0,0 +1,701 @@
+{
+ "type": "conversation.list",
+ "pages": {
+ "type": "pages",
+ "page": 1,
+ "per_page": "100",
+ "total_pages": 1
+ },
+ "total_count": 7,
+ "conversations": [
+ {
+ "type": "conversation",
+ "id": "7",
+ "created_at": 1727737985,
+ "updated_at": 1727737986,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": "conversation",
+ "id": "125299165",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "This is a test example
",
+ "author": {
+ "type": "admin",
+ "id": "7803408",
+ "name": "Kelvin Wari",
+ "email": "kelvin@nango.dev"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": null,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 3
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": null
+ },
+ {
+ "type": "conversation",
+ "id": "6",
+ "created_at": 1727524461,
+ "updated_at": 1727524464,
+ "waiting_since": 1727524463,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "6",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524462,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524462,
+ "first_assignment_at": null,
+ "first_admin_reply_at": 1727524463,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524463,
+ "last_admin_reply_at": 1727524463,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 7
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null
+ },
+ {
+ "type": "conversation",
+ "id": "5",
+ "created_at": 1727524458,
+ "updated_at": 1727524461,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "5",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524459,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524459,
+ "first_assignment_at": 1727524460,
+ "first_admin_reply_at": 1727524459,
+ "first_close_at": null,
+ "last_assignment_at": 1727524460,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524459,
+ "last_admin_reply_at": 1727524459,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 9
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Created by": 7803408,
+ "Ticket category": "Customer ticket",
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 5,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/5",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: Access issue",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "User reports a sudden 'Permission Denied' error while accessing project files, which was previously accessible.",
+ "type": "string"
+ },
+ "Platforms": {
+ "value": null,
+ "type": "string"
+ },
+ "Root cause": {
+ "value": null,
+ "type": "string"
+ }
+ },
+ "state": "in_progress",
+ "ticket_type": "Support Request",
+ "ticket_type_description": "When a customer query can’t be instantly resolved, convert to support request ticket to ensure the right details are captured and allow customers to keep track of progress.",
+ "ticket_type_emoji": "🎟",
+ "ticket_custom_state_admin_label": "In progress",
+ "ticket_custom_state_user_label": "In progress"
+ }
+ },
+ {
+ "type": "conversation",
+ "id": "3",
+ "created_at": 1727524452,
+ "updated_at": 1727524457,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "3",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524453,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524453,
+ "first_assignment_at": 1727524457,
+ "first_admin_reply_at": 1727524454,
+ "first_close_at": null,
+ "last_assignment_at": 1727524457,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524453,
+ "last_admin_reply_at": 1727524454,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 8
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null
+ },
+ {
+ "type": "conversation",
+ "id": "4",
+ "created_at": 1727524454,
+ "updated_at": 1727524457,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "4",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 4
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Ticket category": "Back-office ticket",
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 4,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/4",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: Unauthorized user license charge",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "ℹ️ This is an example of a Back-office ticket.
A back-office ticket can be assigned separately to a back-office team, letting you add your own internal notes. Use them to keep conversations and customer data accessible, so your teams never lose context.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Customer reports an unauthorized user license charge on invoice #12345. The user account is \"User123\".",
+ "type": "string"
+ },
+ "Feature": {
+ "value": null,
+ "type": "string"
+ },
+ "Priority": {
+ "value": "High (needed in 24 hours)",
+ "type": "string"
+ }
+ },
+ "state": "submitted",
+ "ticket_type": "Internal task",
+ "ticket_type_description": "When the support agent need to collaborate with a back-office team to resolve a customer request, create a Back-office internal ticket and assign it to the relevant team.",
+ "ticket_type_emoji": "✅",
+ "ticket_custom_state_admin_label": "Submitted",
+ "ticket_custom_state_user_label": "Submitted"
+ }
+ },
+ {
+ "type": "conversation",
+ "id": "1",
+ "created_at": 1727524446,
+ "updated_at": 1727524452,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "1",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": [
+ {
+ "type": "contact",
+ "id": "66f7ee5ea9e2c41564b4c362"
+ }
+ ]
+ },
+ "first_contact_reply": {
+ "created_at": 1727524448,
+ "type": null,
+ "url": null
+ },
+ "admin_assignee_id": null,
+ "team_assignee_id": 7803411,
+ "open": true,
+ "state": "open",
+ "read": true,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": 1727524448,
+ "first_assignment_at": 1727524452,
+ "first_admin_reply_at": 1727524448,
+ "first_close_at": null,
+ "last_assignment_at": 1727524452,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": 1727524448,
+ "last_admin_reply_at": 1727524448,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 1,
+ "count_conversation_parts": 9
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Language": "English"
+ },
+ "topics": {},
+ "ticket": null
+ },
+ {
+ "type": "conversation",
+ "id": "2",
+ "created_at": 1727524448,
+ "updated_at": 1727524452,
+ "waiting_since": null,
+ "snoozed_until": null,
+ "source": {
+ "type": null,
+ "id": "2",
+ "delivered_as": "admin_initiated",
+ "subject": "",
+ "body": "",
+ "author": {
+ "type": "admin",
+ "id": "7803409",
+ "name": "Operator",
+ "email": "operator+s0lour9i@intercom.io"
+ },
+ "attachments": [],
+ "url": null,
+ "redacted": false
+ },
+ "contacts": {
+ "type": "contact.list",
+ "contacts": []
+ },
+ "first_contact_reply": null,
+ "admin_assignee_id": 7803408,
+ "team_assignee_id": 7803412,
+ "open": true,
+ "state": "open",
+ "read": false,
+ "tags": {
+ "type": "tag.list",
+ "tags": []
+ },
+ "priority": "not_priority",
+ "sla_applied": null,
+ "statistics": {
+ "type": "conversation_statistics",
+ "time_to_assignment": null,
+ "time_to_admin_reply": null,
+ "time_to_first_close": null,
+ "time_to_last_close": null,
+ "median_time_to_reply": null,
+ "first_contact_reply_at": null,
+ "first_assignment_at": null,
+ "first_admin_reply_at": null,
+ "first_close_at": null,
+ "last_assignment_at": null,
+ "last_assignment_admin_reply_at": null,
+ "last_contact_reply_at": null,
+ "last_admin_reply_at": null,
+ "last_close_at": null,
+ "last_closed_by_id": null,
+ "count_reopens": 0,
+ "count_assignments": 0,
+ "count_conversation_parts": 5
+ },
+ "conversation_rating": null,
+ "teammates": {
+ "type": "admin.list",
+ "admins": [
+ {
+ "type": "admin",
+ "id": "7803408"
+ }
+ ]
+ },
+ "title": null,
+ "custom_attributes": {
+ "Copilot used": false,
+ "Ticket category": "Tracker ticket",
+ "Customer reports": 1,
+ "Created by": 7803408
+ },
+ "topics": {},
+ "ticket": {
+ "type": "ticket",
+ "id": 2,
+ "url": "https://app.intercom.com/a/apps/s0lour9i/conversations/2",
+ "custom_attributes": {
+ "_default_title_": {
+ "value": "Example: PDF export broken",
+ "type": "string"
+ },
+ "_default_description_": {
+ "value": "ℹ️ This is an example of a Tracker ticket.
Tracker tickets let you manage all conversations related to a widespread issue with a single ticket. Use them to get all teams working from the same ticket and share updates to all impacted customers at the same time.
🔗 You can see customer conversations that are linked to this ticket in the right-hand sidebar.
--
Chart export feature malfunctioning - exported PDFs are omitting key project milestones and misrepresenting some tasks.",
+ "type": "string"
+ },
+ "Order number": {
+ "value": null,
+ "type": "string"
+ },
+ "Amount": {
+ "value": null,
+ "type": "string"
+ },
+ "Reason": {
+ "value": null,
+ "type": "string"
+ },
+ "Supporting evidence": {
+ "value": null,
+ "type": "files"
+ }
+ },
+ "state": "submitted",
+ "ticket_type": "Bug report",
+ "ticket_type_description": "Capture bug reports that impacts multiple customers. All relevant customer conversations will be linked to a Tracker ticket to act as the single source of truth.",
+ "ticket_type_emoji": "⚠️",
+ "ticket_custom_state_admin_label": "Submitted",
+ "ticket_custom_state_user_label": "Submitted"
+ }
+ }
+ ]
+}
diff --git a/integrations/intercom/nango.yaml b/integrations/intercom/nango.yaml
index cb53156e..9bb0d748 100644
--- a/integrations/intercom/nango.yaml
+++ b/integrations/intercom/nango.yaml
@@ -6,53 +6,131 @@ integrations:
description: |
Fetches a list of conversations from Intercom
output:
- - IntercomConversation
- - IntercomConversationMessage
+ - Conversation
+ - ConversationMessage
sync_type: incremental
+ version: 1.0.0
endpoint:
- - GET /intercom/conversations
- - GET /intercom/conversations-message
+ - GET /conversations
+ - GET /conversation-messages
contacts:
runs: every 6 hours
description: |
Fetches a list of contacts from Intercom
- output: IntercomContact
+ output: Contact
sync_type: full
- endpoint: GET /intercom/contacts
+ track_deletes: true
+ version: 1.0.0
+ endpoint: GET /contacts
+ articles:
+ runs: every 6 hours
+ description: |
+ Fetches a list of articles from Intercom
+ output: Article
+ sync_type: full
+ track_deletes: true
+ version: 1.0.0
+ endpoint: GET /articles
models:
- IntercomContact:
+ Contact:
id: string
workspace_id: string
- external_id: string
+ external_id: string | null
type: string
- email: string | null
+ email: string
phone: string | null
name: string | null
- created_at: date
- updated_at: date
- last_seen_at: date | null
- last_replied_at: date | null
- IntercomConversation:
+ created_at: string
+ updated_at: string
+ last_seen_at: string | null
+ last_replied_at: string | null
+ Conversation:
id: string
- created_at: date
- updated_at: date
- waiting_since: date | null
- snoozed_until: date | null
- title: string
+ created_at: string
+ updated_at: string
+ waiting_since: string | null
+ snoozed_until: string | null
+ title: string | null
contacts:
- contact_id: string
state: string
open: boolean
read: boolean
priority: string
- IntercomConversationMessage:
+ ConversationMessage:
id: string
conversation_id: string
body: string
type: string
- created_at: date
- updated_at: date | null
+ created_at: string
+ updated_at: string
author:
type: string
name: string
id: string
+ Article:
+ type: string
+ id: string
+ workspace_id: string
+ title: string
+ description: string | null
+ body: string | null
+ author_id: number
+ state: string
+ created_at: string
+ updated_at: string
+ url: string | null
+ parent_id: number | null
+ parent_ids: number[]
+ parent_type: string | null
+ default_locale?: string | undefined
+ translated_content?: TranslatedContent | null | undefined
+ ArticleContent:
+ type: string | null
+ title: string
+ description: string
+ body: string
+ author_id: number
+ state: string
+ created_at: number
+ updated_at: number
+ url: string
+ TranslatedContent:
+ type: string | null
+ ar: ArticleContent | null
+ bg: ArticleContent | null
+ bs: ArticleContent | null
+ ca: ArticleContent | null
+ cs: ArticleContent | null
+ da: ArticleContent | null
+ de: ArticleContent | null
+ el: ArticleContent | null
+ en: ArticleContent | null
+ es: ArticleContent | null
+ et: ArticleContent | null
+ fi: ArticleContent | null
+ fr: ArticleContent | null
+ he: ArticleContent | null
+ hr: ArticleContent | null
+ hu: ArticleContent | null
+ id: ArticleContent | null
+ it: ArticleContent | null
+ ja: ArticleContent | null
+ ko: ArticleContent | null
+ lt: ArticleContent | null
+ lv: ArticleContent | null
+ mn: ArticleContent | null
+ nb: ArticleContent | null
+ nl: ArticleContent | null
+ pl: ArticleContent | null
+ pt: ArticleContent | null
+ ro: ArticleContent | null
+ ru: ArticleContent | null
+ sl: ArticleContent | null
+ sr: ArticleContent | null
+ sv: ArticleContent | null
+ tr: ArticleContent | null
+ vi: ArticleContent | null
+ pt-BR: ArticleContent | null
+ zh-CN: ArticleContent | null
+ zh-TW: ArticleContent | null
diff --git a/integrations/intercom/syncs/articles.ts b/integrations/intercom/syncs/articles.ts
new file mode 100644
index 00000000..0ab2a468
--- /dev/null
+++ b/integrations/intercom/syncs/articles.ts
@@ -0,0 +1,36 @@
+import type { NangoSync, Article, ProxyConfiguration } from '../../models';
+import type { IntercomArticle } from '../types';
+import { toArticle } from '../mappers/to-article.js';
+
+/**
+ * Retrieves Intercom articles from the API, transforms the data into a suitable format,
+ * and saves the processed articles using NangoSync. This function handles pagination to ensure
+ * that all articles are fetched, converted, and stored correctly.
+ *
+ * For detailed endpoint documentation, refer to:
+ * https://developers.intercom.com/docs/references/rest-api/api.intercom.io/articles/listarticles
+ *
+ * @param nango An instance of NangoSync for handling synchronization tasks.
+ * @returns Promise that resolves when all articles are fetched and saved.
+ */
+export default async function fetchData(nango: NangoSync): Promise {
+ const config: ProxyConfiguration = {
+ endpoint: '/articles',
+ paginate: {
+ type: 'offset',
+ offset_name_in_request: 'page',
+ response_path: 'data',
+ limit_name_in_request: 'per_page',
+ limit: 100
+ },
+ headers: {
+ 'Intercom-Version': '2.9'
+ },
+ retries: 10
+ };
+
+ for await (const articles of nango.paginate(config)) {
+ const mappedArticles = articles.map((article: IntercomArticle) => toArticle(article));
+ await nango.batchSave(mappedArticles, 'Article');
+ }
+}
diff --git a/integrations/intercom/syncs/contacts.ts b/integrations/intercom/syncs/contacts.ts
index 3477e07b..bc3a408d 100644
--- a/integrations/intercom/syncs/contacts.ts
+++ b/integrations/intercom/syncs/contacts.ts
@@ -1,57 +1,39 @@
-import type { NangoSync, IntercomContact } from '../../models';
+import type { NangoSync, ProxyConfiguration, Contact } from '../../models';
+import { toContact } from '../mappers/to-contact.js';
+import type { IntercomContact } from '../types';
-export default async function fetchData(nango: NangoSync) {
- // Get the list of contacts
- // As of 2023-08-02 the "per_page" parameter is not documented but works
- // https://developers.intercom.com/intercom-api-reference/reference/listcontacts
- let finished = false;
- let nextPage = '';
- while (!finished) {
- // This API endpoint has an annoying bug: If you pass "starting_after" with no value you get a 500 server error
- // Because of this we only set it here when we are fetching page >= 2, otherwise we don't pass it.
- const queryParams: Record = {
- per_page: '150'
- };
+/**
+ * Fetches Intercom contacts, maps them to IntercomContact objects,
+ * and saves the processed contacts using NangoSync.
+ *
+ * This function handles pagination and ensures that all contacts are fetched,
+ * transformed, and stored.
+ *
+ * For endpoint documentation, refer to:
+ * https://developers.intercom.com/docs/references/rest-api/api.intercom.io/contacts/listcontacts
+ *
+ * @param nango An instance of NangoSync for synchronization tasks.
+ * @returns Promise that resolves when all contacts are fetched and saved.
+ */
+export default async function fetchData(nango: NangoSync): Promise {
+ const config: ProxyConfiguration = {
+ endpoint: '/contacts',
+ paginate: {
+ type: 'cursor',
+ cursor_path_in_response: 'pages.next.starting_after',
+ limit_name_in_request: 'per_page',
+ cursor_name_in_request: 'starting_after',
+ response_path: 'data',
+ limit: 100
+ },
+ headers: {
+ 'Intercom-Version': '2.9'
+ },
+ retries: 10
+ };
- if (nextPage !== '') {
- queryParams['starting_after'] = nextPage;
- }
-
- // Make the API request with Nango
- const resp = await nango.get({
- baseUrlOverride: 'https://api.intercom.io/',
- endpoint: 'contacts',
- retries: 5,
- headers: {
- 'Intercom-Version': '2.9'
- },
- params: queryParams
- });
-
- const contacts: any[] = resp.data.data;
- const mappedContacts: IntercomContact[] = contacts.map((contact) => ({
- id: contact.id,
- workspace_id: contact.workspace_id,
- external_id: contact.external_id,
- type: contact.role,
- email: contact.email,
- phone: contact.phone,
- name: contact.name,
- created_at: new Date(contact.created_at * 1000),
- updated_at: new Date(contact.updated_at * 1000),
- last_seen_at: contact.last_seen_at ? new Date(contact.last_seen_at * 1000) : null,
- last_replied_at: contact.last_replied_at ? new Date(contact.last_replied_at * 1000) : null
- }));
-
- // Store this page of conversations in Nango
- await nango.batchSave(mappedContacts, 'IntercomContact');
-
- // Are there more pages?
- // If so, set nextPage to the cursor of the next page
- if (resp.data.pages.next) {
- nextPage = resp.data.pages.next.starting_after;
- } else {
- finished = true;
- }
+ for await (const contacts of nango.paginate(config)) {
+ const mappedContacts = contacts.map((contact: IntercomContact) => toContact(contact));
+ await nango.batchSave(mappedContacts, 'Contact');
}
}
diff --git a/integrations/intercom/syncs/conversations.ts b/integrations/intercom/syncs/conversations.ts
index 7b3f4bfb..ad55f596 100644
--- a/integrations/intercom/syncs/conversations.ts
+++ b/integrations/intercom/syncs/conversations.ts
@@ -1,4 +1,6 @@
-import type { NangoSync, IntercomConversation, IntercomConversationMessage } from '../../models';
+import type { NangoSync, Conversation, ConversationMessage, ProxyConfiguration } from '../../models';
+import { mapConversation, mapMessages } from '../mappers/to-conversation.js';
+import type { IntercomConversationMessage, IntercomConversationsResponse } from '../types';
/**
* Fetches Intercom conversations with all their associated messages and notes.
@@ -7,18 +9,17 @@ import type { NangoSync, IntercomConversation, IntercomConversationMessage } fro
* If a conversation has more than 500 parts some will be missing.
* Only fetches parts that have a message body, ignores parts which are pure actions & metadata (e.g. closed conversation).
*
- * ====
- *
* Initial sync: Fetches conversations updated in the last X years (default: X=2)
* Incremential sync: Fetches the conversations that have been updates since the last sync (updated_at date from Intercom, seems to be reliable)
+ *
+ * For endpoint documentation, refer to:
+ * https://developers.intercom.com/docs/references/rest-api/api.intercom.io/conversations/retrieveconversation
+ * @param nango - An instance of NangoSync for handling synchronization tasks.
*/
-
-export default async function fetchData(nango: NangoSync) {
+export default async function fetchData(nango: NangoSync): Promise {
// Intercom uses unix timestamp for datetimes.
// Convert the last sync run date into a unix timestamp for easier comparison.
const lastSyncDateTimestamp = nango.lastSyncDate ? nango.lastSyncDate.getTime() / 1000 : 0;
-
- // We also define a max sync date for incremental syncs, which is conversations updated in the last X years
const maxYearsToSync = 2;
const maxSyncDate = new Date();
maxSyncDate.setFullYear(new Date().getFullYear() - maxYearsToSync);
@@ -29,158 +30,64 @@ export default async function fetchData(nango: NangoSync) {
// https://developers.intercom.com/intercom-api-reference/reference/listconversations
let finished = false;
let nextPage = '';
+
while (!finished) {
// This API endpoint has an annoying bug: If you pass "starting_after" with no value you get a 500 server error
// Because of this we only set it here when we are fetching page >= 2, otherwise we don't pass it.
const queryParams: Record = {
- per_page: '150'
+ per_page: '100'
};
if (nextPage !== '') {
queryParams['starting_after'] = nextPage;
}
- // Make the API request with Nango
- const resp = await nango.get({
- baseUrlOverride: 'https://api.intercom.io/',
- endpoint: 'conversations',
- retries: 5,
+ const config: ProxyConfiguration = {
+ endpoint: '/conversations',
+ retries: 10,
headers: {
'Intercom-Version': '2.9'
},
params: queryParams
- });
+ };
+ const ConversationResp = await nango.get(config);
+
+ const intercomConversationsPage: Conversation[] = [];
+ const intercomMessagesPage: ConversationMessage[] = [];
- // Let's iterate over the received conversations
- // Then get the details for each.
- const intercomConversationsPage: IntercomConversation[] = [];
- const intercomMessagesPage: IntercomConversationMessage[] = [];
- for (const conversation of resp.data.conversations) {
- // For incremental syncs: Skip conversations that have not been updated since we last synced
- // updated_at is a unix timestamp of the last change to the conversation (e.g. new message from customer, attribute changed)
+ for (const conversation of ConversationResp.data.conversations) {
if (conversation.updated_at < lastSyncDateTimestamp) {
continue;
}
- // Get the details of the conversation
- // https://developers.intercom.com/intercom-api-reference/reference/retrieveconversation
- const conversationResp = await nango.get({
- baseUrlOverride: 'https://api.intercom.io/',
- endpoint: `conversations/${conversation.id}`,
- retries: 5,
+ const conversationConfig: ProxyConfiguration = {
+ endpoint: `/conversations/${conversation.id}`,
+ retries: 10,
headers: {
'Intercom-Version': '2.9'
},
- params: {
- display_as: 'plaintext'
- }
- });
+ params: { display_as: 'plaintext' }
+ };
- // Map the Intercom conversation to our own data model
- intercomConversationsPage.push({
- id: conversationResp.data.id,
- created_at: conversationResp.data.created_at,
- updated_at: conversationResp.data.updated_at,
- waiting_since: conversationResp.data.waiting_since ? conversationResp.data.waiting_since : null,
- snoozed_until: conversationResp.data.snoozed_until ? conversationResp.data.snoozed_until : null,
- title: conversationResp.data.title,
- contacts: conversationResp.data.contacts.contacts.map((contact: any) => {
- return { contact_id: contact.id };
- }),
- state: conversationResp.data.state,
- open: conversationResp.data.open,
- read: conversationResp.data.read,
- priority: conversationResp.data.priority
- });
+ const messageResp = await nango.get(conversationConfig);
- // Map the messages (called "message parts" by Intercom)
- // First message is treated special as the "source" by Intercom
- intercomMessagesPage.push({
- id: conversationResp.data.source.id,
- conversation_id: conversationResp.data.id,
- body: conversationResp.data.source.body,
- type: 'comment',
- created_at: conversationResp.data.created_at,
- updated_at: null,
- author: {
- type: mapAuthorType(conversationResp.data.source.author.type),
- name: conversationResp.data.source.name,
- id: conversationResp.data.source.id
- }
- });
-
- for (const conversationPart of conversationResp.data.conversation_parts.conversation_parts) {
- // Conversation parts can be messages, notes etc. but also actions, such as "closed conversation", "assigned conversation" etc.
- // We only care about the conversation parts where admins and users send a message.
- // For a full list of possible part types see here: https://developers.intercom.com/intercom-api-reference/reference/the-conversation-model#conversation-part-types
- if (conversationPart.body === null) {
- continue;
- }
-
- intercomMessagesPage.push({
- id: conversationPart.id,
- conversation_id: conversationResp.data.id,
- body: conversationPart.body,
- type: mapMessagePartType(conversationPart.part_type),
- created_at: conversationPart.created_at,
- updated_at: conversationPart.updated_at ? conversationPart.updated_at : null,
- author: {
- type: mapAuthorType(conversationPart.author.type),
- name: conversationPart.author.name,
- id: conversationPart.author.id
- }
- });
- }
+ intercomConversationsPage.push(mapConversation(conversation));
+ intercomMessagesPage.push(...mapMessages(messageResp.data));
}
- // Store this page of conversations in Nango
- await nango.batchSave(intercomConversationsPage, 'IntercomConversation');
- await nango.batchSave(intercomMessagesPage, 'IntercomConversationMessage');
+ await nango.batchSave(intercomConversationsPage, 'Conversation');
+ await nango.batchSave(intercomMessagesPage, 'ConversationMessage');
- // Get the last conversation of the page
- // We use this to determine if we should keep on syncing further pages
- const lastConversation = resp.data.conversations.at(-1);
+ const lastConversation = ConversationResp.data.conversations.at(-1);
- // We stop syncing if there are no more pages
- if (!resp.data.pages.next) {
+ if (
+ !ConversationResp.data.pages.next ||
+ (lastSyncDateTimestamp === 0 && lastConversation && lastConversation.updated_at <= maxSyncDateTimestamp) ||
+ (lastSyncDateTimestamp > 0 && lastConversation && lastConversation.updated_at < lastSyncDateTimestamp)
+ ) {
finished = true;
+ } else {
+ nextPage = ConversationResp.data.pages.next.starting_after;
}
-
- // OR one of the following conditions has been reached:
-
- // 1.) We are in an initial sync (last sync timestamp == 0) and we have reached the maxSyncDate
- if (lastSyncDateTimestamp === 0 && lastConversation.updated_at <= maxSyncDateTimestamp) {
- finished = true;
- }
-
- // 2.) We are in an incremental sync and the last conversation on the page is older than our last sync date
- if (lastSyncDateTimestamp > 0 && lastConversation.updated_at < lastSyncDateTimestamp) {
- finished = true;
- }
-
- // None of the above is true, let's fetch the next page
- if (!finished) {
- nextPage = resp.data.pages.next.starting_after;
- }
- }
-}
-
-function mapMessagePartType(rawType: string): string {
- if (rawType === 'assignment') {
- return 'comment';
- } else {
- // Other options with body I have seen: "comment", "note"
- return rawType;
- }
-}
-
-function mapAuthorType(rawType: string): string {
- if (rawType === 'team') {
- return 'admin';
- } else if (rawType === 'lead') {
- return 'user';
- } else {
- // Other options are: "admin", "bot", "user"
- return rawType;
}
}
diff --git a/integrations/intercom/tests/intercom-articles.test.ts b/integrations/intercom/tests/intercom-articles.test.ts
new file mode 100644
index 00000000..b9409375
--- /dev/null
+++ b/integrations/intercom/tests/intercom-articles.test.ts
@@ -0,0 +1,53 @@
+import { vi, expect, it, describe } from "vitest";
+
+import fetchData from "../syncs/articles.js";
+
+describe("intercom articles tests", () => {
+ const nangoMock = new global.vitest.NangoSyncMock({
+ dirname: __dirname,
+ name: "articles",
+ Model: "Article"
+ });
+
+ const models = "Article".split(',');
+ const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
+
+ it("should get, map correctly the data and batchSave the result", async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
+
+ const totalCalls = batchSaveSpy.mock.calls.length;
+
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
+
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
+
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
+ }
+ });
+
+ it('should get, map correctly the data and batchDelete the result', async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
+ }
+ });
+});
diff --git a/integrations/intercom/tests/intercom-contacts.test.ts b/integrations/intercom/tests/intercom-contacts.test.ts
new file mode 100644
index 00000000..bf79e144
--- /dev/null
+++ b/integrations/intercom/tests/intercom-contacts.test.ts
@@ -0,0 +1,53 @@
+import { vi, expect, it, describe } from "vitest";
+
+import fetchData from "../syncs/contacts.js";
+
+describe("intercom contacts tests", () => {
+ const nangoMock = new global.vitest.NangoSyncMock({
+ dirname: __dirname,
+ name: "contacts",
+ Model: "Contact"
+ });
+
+ const models = "Contact".split(',');
+ const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
+
+ it("should get, map correctly the data and batchSave the result", async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
+
+ const totalCalls = batchSaveSpy.mock.calls.length;
+
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
+
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
+
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
+ }
+ });
+
+ it('should get, map correctly the data and batchDelete the result', async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
+ }
+ });
+});
diff --git a/integrations/intercom/tests/intercom-conversations.test.ts b/integrations/intercom/tests/intercom-conversations.test.ts
new file mode 100644
index 00000000..90cc04e3
--- /dev/null
+++ b/integrations/intercom/tests/intercom-conversations.test.ts
@@ -0,0 +1,53 @@
+import { vi, expect, it, describe } from "vitest";
+
+import fetchData from "../syncs/conversations.js";
+
+describe("intercom conversations tests", () => {
+ const nangoMock = new global.vitest.NangoSyncMock({
+ dirname: __dirname,
+ name: "conversations",
+ Model: "Conversation,ConversationMessage"
+ });
+
+ const models = "Conversation,ConversationMessage".split(',');
+ const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
+
+ it("should get, map correctly the data and batchSave the result", async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
+
+ const totalCalls = batchSaveSpy.mock.calls.length;
+
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
+
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
+
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
+ }
+ });
+
+ it('should get, map correctly the data and batchDelete the result', async () => {
+ await fetchData(nangoMock);
+
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
+ }
+ });
+});
diff --git a/integrations/intercom/types.ts b/integrations/intercom/types.ts
new file mode 100644
index 00000000..1ad8c754
--- /dev/null
+++ b/integrations/intercom/types.ts
@@ -0,0 +1,418 @@
+interface ArticleContent {
+ type: string | null;
+ title: string;
+ description: string;
+ body: string;
+ author_id: number;
+ state: string;
+ created_at: number;
+ updated_at: number;
+ url: string;
+}
+
+interface TranslatedContent {
+ type: string | null;
+ ar: ArticleContent | null;
+ bg: ArticleContent | null;
+ bs: ArticleContent | null;
+ ca: ArticleContent | null;
+ cs: ArticleContent | null;
+ da: ArticleContent | null;
+ de: ArticleContent | null;
+ el: ArticleContent | null;
+ en: ArticleContent | null;
+ es: ArticleContent | null;
+ et: ArticleContent | null;
+ fi: ArticleContent | null;
+ fr: ArticleContent | null;
+ he: ArticleContent | null;
+ hr: ArticleContent | null;
+ hu: ArticleContent | null;
+ id: ArticleContent | null;
+ it: ArticleContent | null;
+ ja: ArticleContent | null;
+ ko: ArticleContent | null;
+ lt: ArticleContent | null;
+ lv: ArticleContent | null;
+ mn: ArticleContent | null;
+ nb: ArticleContent | null;
+ nl: ArticleContent | null;
+ pl: ArticleContent | null;
+ pt: ArticleContent | null;
+ ro: ArticleContent | null;
+ ru: ArticleContent | null;
+ sl: ArticleContent | null;
+ sr: ArticleContent | null;
+ sv: ArticleContent | null;
+ tr: ArticleContent | null;
+ vi: ArticleContent | null;
+ 'pt-BR': ArticleContent | null;
+ 'zh-CN': ArticleContent | null;
+ 'zh-TW': ArticleContent | null;
+}
+
+export interface IntercomArticle {
+ type: string;
+ id: string;
+ workspace_id: string;
+ title: string;
+ description: string | null;
+ body: string | null;
+ author_id: number;
+ state: string;
+ created_at: number;
+ updated_at: number;
+ url: string | null;
+ parent_id: number | null;
+ parent_ids: number[];
+ parent_type: string | null;
+ default_locale?: string;
+ translated_content: TranslatedContent | null;
+}
+
+export interface IntercomContact {
+ type: string;
+ id: string;
+ external_id: string | null;
+ workspace_id: string;
+ role: string;
+ email: string;
+ email_domain: string;
+ phone: string | null;
+ formatted_phone: string | null;
+ name: string | null;
+ owner_id: number | null;
+ has_hard_bounced: boolean;
+ marked_email_as_spam: boolean;
+ unsubscribed_from_emails: boolean;
+ created_at: number;
+ updated_at: number;
+ signed_up_at: number | null;
+ last_seen_at: number | null;
+ last_replied_at: number | null;
+ last_contacted_at: number | null;
+ last_email_opened_at: number | null;
+ last_email_clicked_at: number | null;
+ language_override: string | null;
+ browser: string | null;
+ browser_version: string | null;
+ browser_language: string | null;
+ os: string | null;
+ android_app_name: string | null;
+ android_app_version: string | null;
+ android_device: string | null;
+ android_os_version: string | null;
+ android_sdk_version: string | null;
+ android_last_seen_at: number | null;
+ ios_app_name: string | null;
+ ios_app_version: string | null;
+ ios_device: string | null;
+ ios_os_version: string | null;
+ ios_sdk_version: string | null;
+ ios_last_seen_at: number | null;
+ custom_attributes: Record;
+ avatar: Avatar | null;
+ tags: Tags | null;
+ notes: Notes;
+ companies: Companies;
+ location: Location | null;
+ social_profiles: SocialProfiles;
+}
+
+interface Avatar {
+ type: string;
+ image_url: string | null;
+}
+
+interface Tags {
+ data: TagData[];
+ url: string;
+ total_count: number;
+ has_more: boolean;
+}
+
+interface TagData {
+ type: string;
+ id: string;
+ url: string;
+}
+
+interface Notes {
+ data: NoteData[];
+ url: string;
+ total_count: number;
+ has_more: boolean;
+}
+
+interface NoteData {
+ type: string;
+ id: string;
+ url: string;
+}
+
+interface Companies {
+ url: string;
+ total_count: number;
+ has_more: boolean;
+}
+
+interface Location {
+ type: string | null;
+ country: string | null;
+ region: string | null;
+ city: string | null;
+}
+
+interface SocialProfiles {
+ data: SocialProfileData[];
+}
+
+interface SocialProfileData {
+ type: string;
+ name: string;
+ url: string;
+}
+
+enum ConversationState {
+ OPEN = 'open',
+ CLOSED = 'closed',
+ SNOOZED = 'snoozed'
+}
+
+enum ConversationPriority {
+ PRIORITY = 'priority',
+ NOT_PRIORITY = 'not_priority'
+}
+
+enum DeliveryMethod {
+ CUSTOMER_INITIATED = 'customer_initiated',
+ CAMPAIGNS_INITIATED = 'campaigns_initiated',
+ OPERATOR_INITIATED = 'operator_initiated',
+ AUTOMATED = 'automated',
+ ADMIN_INITIATED = 'admin_initiated'
+}
+
+enum TagType {
+ TAG = 'tag'
+}
+
+enum SLAStatus {
+ HIT = 'hit',
+ MISSED = 'missed',
+ CANCELLED = 'cancelled',
+ ACTIVE = 'active'
+}
+
+interface Teammate {
+ type: 'contact';
+ id: string | null;
+}
+
+interface Tag {
+ type: TagType;
+ id: string;
+ name: string;
+ applied_at: number;
+ applied_by: Contact;
+}
+
+interface ConversationRating {
+ rating: number;
+ remark: string;
+ created_at: number;
+ contact: Contact;
+ teammate: Teammate;
+}
+
+interface Attachment {
+ type: string;
+ name: string;
+ url: string;
+ content_type: string;
+ filesize: number;
+ width: number;
+ height: number;
+}
+
+interface ConversationSource {
+ type: 'conversation' | 'email' | 'facebook' | 'instagram' | 'phone_call' | 'phone_switch' | 'push' | 'sms' | 'twitter' | 'whatsapp';
+ id: string;
+ delivered_as: DeliveryMethod;
+ subject: string;
+ body: string;
+ author: Author;
+ attachments: Attachment[];
+ url: string | null;
+ redacted: boolean;
+}
+
+interface ConversationStatistics {
+ type: 'conversation_statistics';
+ time_to_assignment: number;
+ time_to_admin_reply: number;
+ time_to_first_close: number;
+ time_to_last_close: number;
+ median_time_to_reply: number;
+ first_contact_reply_at: number;
+ first_assignment_at: number;
+ first_admin_reply_at: number;
+ first_close_at: number;
+ last_assignment_at: number;
+ last_assignment_admin_reply_at: number;
+ last_contact_reply_at: number;
+ last_admin_reply_at: number;
+ last_close_at: number;
+ last_closed_by_id: string;
+ count_reopens: number;
+ count_assignments: number;
+ count_conversation_parts: number;
+}
+
+interface SLAApplied {
+ type: string;
+ sla_name: string;
+ sla_status: SLAStatus;
+}
+
+interface FirstContactReply {
+ created_at: number;
+ type: string | null;
+ url: string | null;
+}
+
+interface TeammateList {
+ type: 'admin.list';
+ teammates: Teammate[] | null;
+}
+
+interface ConversationPartsResponse {
+ type: 'conversation_part.list';
+ conversation_parts: ConversationPart[];
+ total_count: number;
+}
+
+interface ConversationPart {
+ type: 'conversation_part';
+ id: string;
+ part_type: string;
+ body: string | null;
+ created_at: number;
+ updated_at: number;
+ notified_at: number;
+ assigned_to: Reference | null;
+ author: Author;
+ attachments: Attachment[];
+ external_id: string | null;
+ redacted: boolean;
+}
+
+interface Reference {
+ type: string;
+ id: string | null;
+}
+
+interface Author {
+ type: string;
+ id: string;
+ name: string;
+ email: string;
+}
+
+export interface IntercomConversationMessage {
+ type: 'conversation';
+ id: string;
+ title: string | null;
+ created_at: number;
+ updated_at: number;
+ waiting_since: number | null;
+ snoozed_until: number | null;
+ open: boolean;
+ state: ConversationState;
+ read: boolean;
+ priority: ConversationPriority;
+ admin_assignee_id: number | null;
+ team_assignee_id: string | null;
+ tags: TagList;
+ conversation_rating: ConversationRating | null;
+ source: ConversationSource;
+ contacts: Contacts;
+ teammates: TeammateList | null;
+ custom_attributes: object;
+ first_contact_reply: FirstContactReply | null;
+ sla_applied: SLAApplied | null;
+ statistics: ConversationStatistics | null;
+ conversation_parts: ConversationPartsResponse;
+}
+
+export interface Contact {
+ type: 'contact';
+ id: string;
+ external_id: string | null;
+}
+
+interface Contacts {
+ type: 'contact.list';
+ contacts: Contact[];
+}
+
+interface TagList {
+ type: 'tag.list';
+ tags: Tag[];
+}
+
+interface LinkedObjects {
+ type: 'list';
+ data: any[];
+ total_count: number;
+ has_more: boolean;
+}
+
+export interface IntercomConversation {
+ type: 'conversation';
+ id: string;
+ created_at: number;
+ updated_at: number;
+ waiting_since: number | null;
+ snoozed_until: number | null;
+ source: ConversationSource;
+ contacts: Contacts;
+ first_contact_reply: FirstContactReply | null;
+ admin_assignee_id: string | null;
+ team_assignee_id: string | null;
+ open: boolean;
+ state: ConversationState;
+ read: boolean;
+ tags: TagList;
+ priority: ConversationPriority;
+ sla_applied: SLAApplied | null;
+ statistics: ConversationStatistics | null;
+ conversation_rating: ConversationRating | null;
+ teammates: TeammateList | null;
+ title: string | null;
+ custom_attributes: object;
+ topics: object;
+ ticket: string | null;
+ linked_objects: LinkedObjects;
+ ai_agent: string | null;
+ ai_agent_participated: boolean;
+}
+
+interface IntercomNextPage {
+ page: number;
+ starting_after: string;
+}
+
+interface IntercomPages {
+ type: 'pages';
+ next?: IntercomNextPage;
+ page: number;
+ per_page: number;
+ total_pages: number;
+}
+
+export interface IntercomConversationsResponse {
+ type: 'conversation.list';
+ pages: IntercomPages;
+ total_count: number;
+ conversations: IntercomConversation[];
+}
diff --git a/integrations/salesforce/mocks/deals/batchDelete.json b/integrations/notion/mocks/content-metadata/ContentMetadata/batchDelete.json
similarity index 100%
rename from integrations/salesforce/mocks/deals/batchDelete.json
rename to integrations/notion/mocks/content-metadata/ContentMetadata/batchDelete.json
diff --git a/integrations/notion/mocks/content-metadata/batchSave.json b/integrations/notion/mocks/content-metadata/ContentMetadata/batchSave.json
similarity index 100%
rename from integrations/notion/mocks/content-metadata/batchSave.json
rename to integrations/notion/mocks/content-metadata/ContentMetadata/batchSave.json
diff --git a/integrations/notion/tests/notion-content-metadata.test.ts b/integrations/notion/tests/notion-content-metadata.test.ts
index acfb8e73..5e10418f 100644
--- a/integrations/notion/tests/notion-content-metadata.test.ts
+++ b/integrations/notion/tests/notion-content-metadata.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/content-metadata.js";
@@ -10,39 +9,45 @@ describe("notion content-metadata tests", () => {
Model: "ContentMetadata"
});
+ const models = "ContentMetadata".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "ContentMetadata");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "ContentMetadata");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/salesforce/mocks/tickets/batchDelete.json b/integrations/salesforce/mocks/accounts/Account/batchDelete.json
similarity index 100%
rename from integrations/salesforce/mocks/tickets/batchDelete.json
rename to integrations/salesforce/mocks/accounts/Account/batchDelete.json
diff --git a/integrations/salesforce/mocks/accounts/batchSave.json b/integrations/salesforce/mocks/accounts/Account/batchSave.json
similarity index 100%
rename from integrations/salesforce/mocks/accounts/batchSave.json
rename to integrations/salesforce/mocks/accounts/Account/batchSave.json
diff --git a/integrations/xero/mocks/accounts/batchDelete.json b/integrations/salesforce/mocks/articles/Article/batchDelete.json
similarity index 100%
rename from integrations/xero/mocks/accounts/batchDelete.json
rename to integrations/salesforce/mocks/articles/Article/batchDelete.json
diff --git a/integrations/salesforce/mocks/articles/batchSave.json b/integrations/salesforce/mocks/articles/Article/batchSave.json
similarity index 100%
rename from integrations/salesforce/mocks/articles/batchSave.json
rename to integrations/salesforce/mocks/articles/Article/batchSave.json
diff --git a/integrations/xero/mocks/contacts/batchDelete.json b/integrations/salesforce/mocks/contacts/Contact/batchDelete.json
similarity index 100%
rename from integrations/xero/mocks/contacts/batchDelete.json
rename to integrations/salesforce/mocks/contacts/Contact/batchDelete.json
diff --git a/integrations/salesforce/mocks/contacts/batchSave.json b/integrations/salesforce/mocks/contacts/Contact/batchSave.json
similarity index 100%
rename from integrations/salesforce/mocks/contacts/batchSave.json
rename to integrations/salesforce/mocks/contacts/Contact/batchSave.json
diff --git a/integrations/xero/mocks/invoices/batchDelete.json b/integrations/salesforce/mocks/deals/Deal/batchDelete.json
similarity index 100%
rename from integrations/xero/mocks/invoices/batchDelete.json
rename to integrations/salesforce/mocks/deals/Deal/batchDelete.json
diff --git a/integrations/salesforce/mocks/deals/batchSave.json b/integrations/salesforce/mocks/deals/Deal/batchSave.json
similarity index 100%
rename from integrations/salesforce/mocks/deals/batchSave.json
rename to integrations/salesforce/mocks/deals/Deal/batchSave.json
diff --git a/integrations/xero/mocks/items/batchDelete.json b/integrations/salesforce/mocks/tickets/Ticket/batchDelete.json
similarity index 100%
rename from integrations/xero/mocks/items/batchDelete.json
rename to integrations/salesforce/mocks/tickets/Ticket/batchDelete.json
diff --git a/integrations/salesforce/mocks/tickets/batchSave.json b/integrations/salesforce/mocks/tickets/Ticket/batchSave.json
similarity index 100%
rename from integrations/salesforce/mocks/tickets/batchSave.json
rename to integrations/salesforce/mocks/tickets/Ticket/batchSave.json
diff --git a/integrations/salesforce/tests/salesforce-accounts.test.ts b/integrations/salesforce/tests/salesforce-accounts.test.ts
index e39eb523..7e87f25d 100644
--- a/integrations/salesforce/tests/salesforce-accounts.test.ts
+++ b/integrations/salesforce/tests/salesforce-accounts.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/accounts.js";
@@ -10,39 +9,45 @@ describe("salesforce accounts tests", () => {
Model: "Account"
});
+ const models = "Account".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Account");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Account");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/salesforce/tests/salesforce-articles.test.ts b/integrations/salesforce/tests/salesforce-articles.test.ts
index ed2ecf89..91f7d0b7 100644
--- a/integrations/salesforce/tests/salesforce-articles.test.ts
+++ b/integrations/salesforce/tests/salesforce-articles.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/articles.js";
@@ -10,39 +9,45 @@ describe("salesforce articles tests", () => {
Model: "Article"
});
+ const models = "Article".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Article");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Article");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/salesforce/tests/salesforce-contacts.test.ts b/integrations/salesforce/tests/salesforce-contacts.test.ts
index 833e5538..d25e7297 100644
--- a/integrations/salesforce/tests/salesforce-contacts.test.ts
+++ b/integrations/salesforce/tests/salesforce-contacts.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/contacts.js";
@@ -10,39 +9,45 @@ describe("salesforce contacts tests", () => {
Model: "Contact"
});
+ const models = "Contact".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Contact");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Contact");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/salesforce/tests/salesforce-deals.test.ts b/integrations/salesforce/tests/salesforce-deals.test.ts
index d96ace7b..1857ffd1 100644
--- a/integrations/salesforce/tests/salesforce-deals.test.ts
+++ b/integrations/salesforce/tests/salesforce-deals.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/deals.js";
@@ -10,39 +9,45 @@ describe("salesforce deals tests", () => {
Model: "Deal"
});
+ const models = "Deal".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Deal");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Deal");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/salesforce/tests/salesforce-tickets.test.ts b/integrations/salesforce/tests/salesforce-tickets.test.ts
index c12eb6bb..afb9047b 100644
--- a/integrations/salesforce/tests/salesforce-tickets.test.ts
+++ b/integrations/salesforce/tests/salesforce-tickets.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/tickets.js";
@@ -10,39 +9,45 @@ describe("salesforce tickets tests", () => {
Model: "Ticket"
});
+ const models = "Ticket".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Ticket");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Ticket");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/xero/mocks/payments/batchDelete.json b/integrations/xero/mocks/accounts/Account/batchDelete.json
similarity index 100%
rename from integrations/xero/mocks/payments/batchDelete.json
rename to integrations/xero/mocks/accounts/Account/batchDelete.json
diff --git a/integrations/xero/mocks/accounts/batchSave.json b/integrations/xero/mocks/accounts/Account/batchSave.json
similarity index 100%
rename from integrations/xero/mocks/accounts/batchSave.json
rename to integrations/xero/mocks/accounts/Account/batchSave.json
diff --git a/integrations/xero/mocks/contacts/Contact/batchDelete.json b/integrations/xero/mocks/contacts/Contact/batchDelete.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/integrations/xero/mocks/contacts/Contact/batchDelete.json
@@ -0,0 +1 @@
+[]
diff --git a/integrations/xero/mocks/contacts/batchSave.json b/integrations/xero/mocks/contacts/Contact/batchSave.json
similarity index 100%
rename from integrations/xero/mocks/contacts/batchSave.json
rename to integrations/xero/mocks/contacts/Contact/batchSave.json
diff --git a/integrations/xero/mocks/invoices/Invoice/batchDelete.json b/integrations/xero/mocks/invoices/Invoice/batchDelete.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/integrations/xero/mocks/invoices/Invoice/batchDelete.json
@@ -0,0 +1 @@
+[]
diff --git a/integrations/xero/mocks/invoices/batchSave.json b/integrations/xero/mocks/invoices/Invoice/batchSave.json
similarity index 100%
rename from integrations/xero/mocks/invoices/batchSave.json
rename to integrations/xero/mocks/invoices/Invoice/batchSave.json
diff --git a/integrations/xero/mocks/items/Item/batchDelete.json b/integrations/xero/mocks/items/Item/batchDelete.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/integrations/xero/mocks/items/Item/batchDelete.json
@@ -0,0 +1 @@
+[]
diff --git a/integrations/xero/mocks/items/batchSave.json b/integrations/xero/mocks/items/Item/batchSave.json
similarity index 100%
rename from integrations/xero/mocks/items/batchSave.json
rename to integrations/xero/mocks/items/Item/batchSave.json
diff --git a/integrations/xero/mocks/payments/Payment/batchDelete.json b/integrations/xero/mocks/payments/Payment/batchDelete.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/integrations/xero/mocks/payments/Payment/batchDelete.json
@@ -0,0 +1 @@
+[]
diff --git a/integrations/xero/mocks/payments/batchSave.json b/integrations/xero/mocks/payments/Payment/batchSave.json
similarity index 100%
rename from integrations/xero/mocks/payments/batchSave.json
rename to integrations/xero/mocks/payments/Payment/batchSave.json
diff --git a/integrations/xero/tests/xero-accounts.test.ts b/integrations/xero/tests/xero-accounts.test.ts
index 5bc36b09..694353a5 100644
--- a/integrations/xero/tests/xero-accounts.test.ts
+++ b/integrations/xero/tests/xero-accounts.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/accounts.js";
@@ -10,39 +9,45 @@ describe("xero accounts tests", () => {
Model: "Account"
});
+ const models = "Account".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Account");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Account");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/xero/tests/xero-contacts.test.ts b/integrations/xero/tests/xero-contacts.test.ts
index db363363..77336f5a 100644
--- a/integrations/xero/tests/xero-contacts.test.ts
+++ b/integrations/xero/tests/xero-contacts.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/contacts.js";
@@ -10,39 +9,45 @@ describe("xero contacts tests", () => {
Model: "Contact"
});
+ const models = "Contact".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Contact");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Contact");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/xero/tests/xero-invoices.test.ts b/integrations/xero/tests/xero-invoices.test.ts
index 7b3a4d15..268ed3ba 100644
--- a/integrations/xero/tests/xero-invoices.test.ts
+++ b/integrations/xero/tests/xero-invoices.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/invoices.js";
@@ -10,39 +9,45 @@ describe("xero invoices tests", () => {
Model: "Invoice"
});
+ const models = "Invoice".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Invoice");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Invoice");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/xero/tests/xero-items.test.ts b/integrations/xero/tests/xero-items.test.ts
index 400d1fbe..8e48e212 100644
--- a/integrations/xero/tests/xero-items.test.ts
+++ b/integrations/xero/tests/xero-items.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/items.js";
@@ -10,39 +9,45 @@ describe("xero items tests", () => {
Model: "Item"
});
+ const models = "Item".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Item");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Item");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/integrations/xero/tests/xero-payments.test.ts b/integrations/xero/tests/xero-payments.test.ts
index 00295d20..d37573d4 100644
--- a/integrations/xero/tests/xero-payments.test.ts
+++ b/integrations/xero/tests/xero-payments.test.ts
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/payments.js";
@@ -10,39 +9,45 @@ describe("xero payments tests", () => {
Model: "Payment"
});
+ const models = "Payment".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "Payment");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "Payment");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/scripts/tests/generate-tests.ts b/scripts/tests/generate-tests.ts
index 2732d4e8..c81813ff 100644
--- a/scripts/tests/generate-tests.ts
+++ b/scripts/tests/generate-tests.ts
@@ -20,11 +20,11 @@ for (const integration in integrations) {
}
}
-function generateSyncTest(integration: string, syncName: string, modelName: string) {
+function generateSyncTest(integration: string, syncName: string, modelName: string | string[]) {
const data: {
integration: string;
syncName: string;
- modelName: string;
+ modelName: string | string[];
} = {
integration,
syncName,
diff --git a/scripts/tests/sync-template.ejs b/scripts/tests/sync-template.ejs
index 52a16915..5b13f41a 100644
--- a/scripts/tests/sync-template.ejs
+++ b/scripts/tests/sync-template.ejs
@@ -1,5 +1,4 @@
import { vi, expect, it, describe } from "vitest";
-import type { NangoSync } from "../models.js";
import fetchData from "../syncs/<%= syncName %>.js";
@@ -10,39 +9,45 @@ describe("<%= integration %> <%= syncName %> tests", () => {
Model: "<%= modelName %>"
});
+ const models = "<%= modelName %>".split(',');
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave');
it("should get, map correctly the data and batchSave the result", async () => {
await fetchData(nangoMock);
- const batchSaveData = await nangoMock.getBatchSaveData();
+ for (const model of models) {
+ const batchSaveData = await nangoMock.getBatchSaveData(model);
- const totalCalls = batchSaveSpy.mock.calls.length;
+ const totalCalls = batchSaveSpy.mock.calls.length;
- if (totalCalls > 1) {
- const splitSize = Math.ceil(batchSaveData.length / totalCalls);
+ if (totalCalls > models.length) {
+ const splitSize = Math.ceil(batchSaveData.length / totalCalls);
- const splitBatchSaveData = [];
- for (let i = 0; i < totalCalls; i++) {
- const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
- splitBatchSaveData.push(chunk);
- }
+ const splitBatchSaveData = [];
+ for (let i = 0; i < totalCalls; i++) {
+ const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize);
+ splitBatchSaveData.push(chunk);
+ }
- splitBatchSaveData.forEach((data, index) => {
- expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
- });
+ splitBatchSaveData.forEach((data, index) => {
+ // @ts-ignore
+ expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data);
+ });
- } else {
- expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, "<%= modelName %>");
+ } else {
+ expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model);
+ }
}
});
it('should get, map correctly the data and batchDelete the result', async () => {
await fetchData(nangoMock);
- const batchDeleteData = await nangoMock.getBatchDeleteData();
- if (batchDeleteData && batchDeleteData.length > 0) {
- expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, "<%= modelName %>");
+ for (const model of models) {
+ const batchDeleteData = await nangoMock.getBatchDeleteData(model);
+ if (batchDeleteData && batchDeleteData.length > 0) {
+ expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model);
+ }
}
});
});
diff --git a/vitest.setup.ts b/vitest.setup.ts
index 35a4a5af..10d7688e 100644
--- a/vitest.setup.ts
+++ b/vitest.setup.ts
@@ -26,8 +26,8 @@ export class NangoSyncMock {
this.dirname = dirname;
this.name = name;
this.Model = Model;
- this.batchSave = vi.fn(this.getBatchSaveData.bind(this));
- this.batchDelete = vi.fn(this.getBatchDeleteData.bind(this));
+ this.batchSave = vi.fn();
+ this.batchDelete = vi.fn();
this.getConnection = vi.fn(this.getConnectionData.bind(this));
this.getMetadata = vi.fn(this.getMetadataData.bind(this));
this.paginate = vi.fn(this.getProxyPaginateData.bind(this));
@@ -50,13 +50,13 @@ export class NangoSyncMock {
}
}
- private async getBatchSaveData() {
- const data = await this.getMockFile(`${this.name}/batchSave`);
+ public async getBatchSaveData(modelName: string) {
+ const data = await this.getMockFile(`${this.name}/${modelName}/batchSave`);
return data;
}
- private async getBatchDeleteData() {
- const data = await this.getMockFile(`${this.name}/batchDelete`);
+ public async getBatchDeleteData(modelName: string) {
+ const data = await this.getMockFile(`${this.name}/${modelName}/batchDelete`);
return data;
}