AI Prompt File
v0.1
# 🧠 AI Bootstrap Guide for Building Front Plugins
This document is designed to help AI agents scaffold production-ready plugins for Front using the Plugin SDK. Developers can feed this file to an AI tool (like ChatGPT or GitHub Copilot) as a base prompt, then follow up with instructions such as:
> Create a sidebar plugin for booking hotel reservations.
> Scaffold a composer plugin that inserts a custom templated reply.
---
## 🚀 What You’re Building
You’re generating a **Front plugin** using the official [Front Plugin SDK](https://dev.frontapp.com/docs/plugin-overview) available as an npm package `@frontapp/plugin-sdk`. Front plugins connect to external systems or extend Front’s UI, and are rendered in an iframe embedded inside Front.
**Two plugin types exist**:
- **Sidebar plugin**: Appears in the Front sidebar to add external context and functions to the Front teammate's selected conversation (or conversations).
- **Composer plugin**: Appears in the message composer to provide external context and functionality to the draft or reply being composed.
Use the plugin type specified in the user’s request.
---
## 📐 Output Format
You must produce **two outputs**:
1. **Code scaffold** in TypeScript using the Front Plugin SDK
2. A **Markdown plan file** describing:
- Plugin functionality
- Phased milestones
- Implementation checklist
- Edge cases to handle
- Any assumptions made
- A full list of the Plugin SDK methods used in the plugin
Example filename: `plugin-plan.md`
After everything is done, please include instructions for running the plugin both in the terminal (if you are running in the terminal) and in a readme:
- Instructions for running the plugin locally, both in debug mode and prod mode. Debug mode should run with a query string appended to the plugin URL, like so: ?debug=true.
- Instructions for creating a Front developer app as described at https://dev.frontapp.com/docs/create-and-manage-apps#create-an-app
- Instructions for adding a plugin feature (with the plugin URL set to the appropriate localhost address) to the app as described at https://dev.frontapp.com/docs/create-and-manage-apps#create-a-plugin
Here is an example of good instructions you should include both in the readme and in the terminal where you are being run. Make sure to adapt the terminal commands if the developer specified a framework other than React.
To run the plugin:
1. Run `npm install`
2. Run `npm run dev`
3. Note the localhost URL where the plugin is running locally (for example, `http://localhost:3000`)
4. In your Front company settings, click Developers.
5. Create a new app.
6. Add a plugin feature to the app.
7. Configure the plugin URL to where your plugin is running locally (typically `http://localhost:3000`)
8. If you want to run the plugin in debug mode, append `?debug=true` to the plugin URL.
9. Open the sidebar plugin in your Front workspace to see it in action.
---
## ⚙️ Technical Stack
Use the following technologies unless the developer specifies otherwise:
- TypeScript (strict mode optional)
- React (latest)
- Use `@frontapp/plugin-sdk`
- Use Front SDK methods and data models / interfaces whenever possible (these are detailed further down)
- Use `lucide-react` for icons unless otherwise specified
- Framework: use plain React or Next.js App Router if specified
- The developer should be able to run the plugin in debug mode to expose plugin tabs useful for development (described in more detail below)
Iframe Embedding:
Configure appropriate Content Security Policy (CSP) headers so the plugin loads correctly in Front’s iframe.
---
## 🗂 Plugin Plan Template
After generating the code, also create a file called `plugin-plan.md` with the following structure:
```markdown
# 📋 Plugin Plan
## Overview
- Purpose of the plugin
- What user problems it solves
## Milestones / Phases
1. Scaffold plugin with SDK, auth, and context
2. Build UI
3. Add API integration logic and main plugin functionality
4. Add error handling and loading states
5. Add edge case handling (e.g., no conversation, API failures)
## Checklist
- [ ] Correct plugin type used (sidebar/composer)
- [ ] SDK loaded dynamically
- [ ] Subscribes to contextUpdates
- [ ] Renders user and conversation info
- [ ] Uses Front SDK methods and data models / interfaces when applicable
- [ ] Error handling and fallbacks
- [ ] Dark mode support
- [ ] Fully typed in TypeScript
## Assumptions
- Any assumptions about APIs, user behavior, plugin goals
```
---
## 🧪 Example Prompts
Below are example prompts a developer might use after loading this guide:
> "Create a sidebar plugin that shows customer order history from our Shopify integration."
> "Build a compose plugin that suggests AI-generated replies based on message subject and recipient."
> "Make a plugin that tags conversations and assigns a teammate based on message keywords."
---
## 📘 Reference Resources
- [Front Plugin SDK Overview](https://dev.frontapp.com/docs/plugin-overview)
- [Plugin SDK Methods Reference Documentation](https://dev.frontapp.com/reference/plugin-sdk-objects)
- [Plugin Getting Started Example (GitHub)](https://github.com/frontapp/plugin-getting-started)
- [plugin-sdk npm package](https://www.npmjs.com/package/@frontapp/plugin-sdk?activeTab=code)
## 🧱 Plugin Architecture & Best Practices
- Dynamically import the Front SDK:
```ts
const { default: Front } = await import("@frontapp/plugin-sdk");
```
- Wrap SDK usage in `try/catch` blocks for resilience
- Subscribe to `contextUpdates` in `useEffect` on mount
- Unsubscribe on unmount
- Always check for `context.conversation` or `context.teammate`
- Use Front SDK methods (such as `createDraft`, `listMessages`, `openUrl`, etc.) and data models / interfaces (such as `context`, `context.entryPoint`, `context.teammate`, etc. whenever possible (these are detailed further down)
- Support dark/light mode switching
- Avoid horizontal scrolling (plugins appear in a narrow sidebar)
### ⏳ LAZY LOADING UX ENHANCEMENT
- Show a branded loading state or skeleton loader while initializing.
- Never show a blank screen.
- Optionally display a friendly message:
“Loading plugin… please wait.”
- Do not hallucinate how to initialize the Plugin SDK. Follow the exact instructions and code patterns in the code samples included in this readme. The SDK is not initialized with an init() function, for example, so don't use that. Reference the sample code in this prompt file.
---
## 🧩 FUNCTIONALITY
This section specifies some high-level functionality and visual architecture that the plugin should scaffold unless the developer specifies otherwise or it conflicts with the developer's specified use case. The first tab is the primary tab and the other tab is a debug tab available in debug mode, which should only show if run in debug mode.
### Main plugin panel (tab 1)
This panel should show the main use case that the developer has asked you to create. This panel should always display by default.
### 🧍 Debug panel (Tab 2)
This tab provides neatly arranged, expandable sections as detailed below in each H4 section.
#### Teammate information:
- Teammate name
- Teammate email
- Teammate username
- Teammate ID
#### Conversation Details:
- Subject
- Status
- Recipient (name + email)
- Assignee (with fallback if none)
- Tags (with fallback)
- Conversation ID
- Type
- Blurb
- Inboxes
- links
#### Attachments
Show this section only when attachments exist.
Fetch all messages with context.listMessages()
For each message:
- Inspect message.content.attachments
- If attachments exist, show:
- Filename
- MIME type
- Size
- Download URL
- is_inline and is_deleted
- Message sender (if available)
Show this panel only when attachments exist.
#### 🛠 Technical Documentation
This section provides documentation about the plugin architecture implemented.
Provide:
- Architecture overview
- SDK usage details
- State handling approach
- CSP considerations
#### 🧪 Debug Console
This section helps developers debug what's being rendered in the Front plugin context.
The debug console should include a live context JSON display:
Short example below, but include everything the Front SDK is returning in the debug console:
**Tip:** Use a collapsible JSON viewer component to keep things tidy.
```json
{
"type": "singleConversation",
"conversation": { ... },
"teammate": { ... }
}
```
Include real-time logs:
- Context updates (every change, clearly timestamped)
- SDK calls (e.g., listMessages(), contextUpdates)
- Any errors or warnings
Include plugin status indicator:
- Waiting for SDK
- SDK Initialized
- Context Received
- No Conversation Selected
Include message counts:
- Total messages fetched
- Total attachments detected
Include attachment debug:
- List of all attachment metadata (e.g., filenames, MIME types, size)
Include clear errors with stack traces when SDK calls fail
### 🎛️ CUSTOMIZABLE INTERFACE
Allow users to drag and reorder sections vertically (Conversation Info, User Info, Attachments, Debug Console, etc.).
Store layout preferences in localStorage or indexedDB.
Persist layout across plugin reloads.
### 🖼️ UI/UX DESIGN
Use this visual style unless the developer specifies otherwise:
- Modern, clean, and minimal.
- No harsh or overly bright color combinations.
- Avoid high-contrast colors like green-on-blue.
- Use neutral backgrounds (light or dark) with high-legibility text.
- Support Light/Dark mode toggle, updating styles dynamically.
- No horizontal scrolling—everything should stack responsively.
- Use lucide-react icons consistently.
- Clear visual hierarchy and generous spacing for readability.
- You should avoid pop ups / alerts / modal windows but if you have to use them, use the `openUrlInPopup` SDK method, otherwise you'll encounter errors where the modal won't open in the plugin window.
- Take extra care to make composer plugins responsive. They open in very small modals in the message toolbar.
- I repeat, no horizontal scrolling!
Examples of acceptable colors:
- Soft gray backgrounds
- Subtle accent colors (e.g., blue or green for icons, but not together)
- Muted highlights
#### 🎯 Composer Plugin UI Requirements
These recommendations are specific to composer plugins. If a developer asks for a composer plugin, please pay careful attention to them. If a developer asks for a sidebar plugin (more common), please ignore them.
**Start of composer plugin UI requirements**
Critical Layout Constraints:
- Add overflow-x: hidden to body, #root, and main App container
- Use * { box-sizing: border-box; max-width: 100%; } as global CSS rule
- Set minimum width to 280px maximum (composer modals are extremely
narrow)
- Apply min-width: 0 to flex children that contain text to enable proper
overflow handling
Text Overflow Protection:
- Always use text-overflow: ellipsis and white-space: nowrap on text that
could be long (filenames, emails, URLs)
- Truncate long content types/metadata at ~20 characters with "..."
- Use line breaks strategically instead of inline separators (• symbols)
to save horizontal space
- Keep button text extremely short ("Encrypt All" not "Encrypt All
Attachments")
Spacing Optimization:
- Use 0.375rem max padding for main containers (not 0.5rem+)
- Use 0.25rem gaps between elements (not 0.5rem+)
- Use 0.125rem margins between small items
- Reduce font sizes significantly: 0.8125rem for main text,
0.625-0.6875rem for buttons/metadata
Flex Layout Best Practices:
- Use flex-shrink: 0 on buttons, badges, and action elements to prevent
compression
- Use align-items: flex-start instead of center in tight horizontal
layouts
- Apply min-width: 0 to text containers to allow proper text truncation
- Use justify-content: space-between carefully - ensure content can
actually fit
Icon Sizing:
- Use w-2.5 h-2.5 (10px) for small action buttons
- Use w-3 h-3 (12px) maximum for primary actions
- Use w-4 h-4 (16px) maximum for headers/titles
Content Strategy:
- Break long metadata onto multiple lines rather than trying to fit
horizontally
- Prioritize essential information - hide secondary details if space is
constrained
- Use progressive disclosure (show details on hover/click) rather than
cramming everything visible
Testing Requirement:
- Always test at 280px width minimum to simulate smallest composer modal
- Verify no horizontal scroll bars appear at any common viewport width
- Test with long filenames, email addresses, and content types to ensure
graceful truncation
**End of composer plugin UI requirements**
### 🧯 ERROR HANDLING & EDGE CASES
- If no conversation is selected:“Please select a conversation to view details.”
Handle missing/incomplete data:
- Fallbacks for assignee, tags, recipients, attachments.
- Gracefully catch all SDK and runtime errors:
- Show clear, actionable error messages in the UI.
- Log stack traces to the Debug Console.
### 📌 FINAL REQUIREMENTS
- Fully production-ready, maintainable codebase.
- Modular React components.
- Robust error and edge-case handling.
- Context-aware, iframe-friendly.
- High usability and developer introspection via the Debug Console.
### 📦 Patterns from the Starter Plugin
These are the programming patterns we demonstrate in our Getting Started plugin. They should be followed unless the developer specifies a different pattern.
**Context Subscription Example:**
```ts
useEffect(() => {
const sub = Front.contextUpdates.subscribe((ctx) => {
setContext(ctx);
});
return () => sub.unsubscribe();
}, []);
```
**Display Fallback UI if No Conversation**
```tsx
if (!context?.conversation) {
return <div>Select a conversation to get started.</div>;
}
```
Here are more details on the architectural patterns that you should use. These patterns are what we recommend developers to follow in our Getting Started guide, so you can use this code architecture to scaffold a plugin when the developer prompts you. You can follow the patterns shown in this section but adapt the components folder and titles, labels, text descriptions, etc., to fit the developer use case.
index.html
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
```
src/App.jsx
```jsx
import './App.css';
import Tutorial from './components/Tutorial';
import { useFrontContext } from './providers/frontContext';
function App() {
const context = useFrontContext();
if (!context)
return (
<div className="App">
<p>Waiting to connect to the Front context.</p>
</div>
)
switch(context.type) {
case 'noConversation':
return (
<div className="App">
<p>No conversation selected. Select a conversation to use this plugin.</p>
</div>
);
case 'singleConversation':
return (
<div className="App">
<Tutorial />
</div>
);
case 'multiConversations':
return (
<div className="App">
<p>Multiple conversations selected. Select only one conversation to use this plugin.</p>
</div>
);
default:
console.error(`Unsupported context type: ${context.type}`);
break;
};
}
export default App;
```
src/main.js
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {FrontContextProvider} from './providers/frontContext';
ReactDOM.render(
<React.StrictMode>
<FrontContextProvider>
<App />
</FrontContextProvider>
</React.StrictMode>,
document.getElementById('root')
);
```
src/providers/frontContext.jsx
```jsx
import {createContext, useContext, useEffect, useState} from 'react';
import Front from '@frontapp/plugin-sdk';
/*
* Context.
*/
export const FrontContext = createContext();
/*
* Hook.
*/
export function useFrontContext() {
return useContext(FrontContext);
}
/*
* Component.
*/
export const FrontContextProvider = ({children}) => {
const [context, setContext] = useState();
useEffect(() => {
const subscription = Front.contextUpdates.subscribe(frontContext => {
setContext(frontContext);
})
return () => subscription.unsubscribe();
}, [])
return (
<FrontContext.Provider value={context}>
{children}
</FrontContext.Provider>
)
}
```
src/components/Tutorial.jsx
(this is just an example component to show the general file relationships...components would have to be adapted to fit the use case the developer requests for the plugin)
```jsx
import React, {useEffect, useState} from 'react';
import { useFrontContext } from '../providers/frontContext';
import {Paragraph, Heading, Button} from '@frontapp/ui-kit';
function Tutorial() {
const context = useFrontContext();
const [companyStats, setCompanyStats] = useState({});
const [latestMessageId, setLatestMessageId] = useState();
const user = (context.teammate && context.teammate.name) ? context.teammate.name : 'world';
const recipient = (context.conversation && context.conversation.recipient && context.conversation.recipient.name) ? context.conversation.recipient.name : 'there';
// Watches the context and selected the latest message id from the available messages.
useEffect(() => {
context.listMessages()
.then(response => {
if (response.results.length > 0) {
const latestMessageIndex = response.results.length - 1;
setLatestMessageId(response.results[latestMessageIndex].id)
} else {
setLatestMessageId(undefined);
}
});
}, [context]);
useEffect(() => {
// Pseudo-code for fetching data from an external API or database
setCompanyStats(
{
'company': 'Blue Rose Labs',
'accountNumber': 54968483,
'activeOrder': 8347,
'status': 'Shipped',
'deliveryDate': 'March 31st'
}
);
}, []);
const onCreateDraftClick = () => {
if (!latestMessageId)
return;
context.createDraft({
content: {
body: `Hello ${recipient}! Order ${companyStats.activeOrder} is ${companyStats.status} and expected to arrive on ${companyStats.deliveryDate}.`,
type: 'text'
},
replyOptions: {
type: 'replyAll',
originalMessageId: latestMessageId
}
})
};
return (
<div className="App">
<Paragraph>Hello {user}!</Paragraph>
<h4>Contact details:</h4>
<table>
<tbody>
<tr>
<td><Heading>Company</Heading></td>
<td>{companyStats.company}</td>
</tr>
<tr>
<td><Heading>Account number</Heading></td>
<td>{companyStats.accountNumber}</td>
</tr>
<tr>
<td><Heading>Active order</Heading></td>
<td><a href="https://example.com">{companyStats.activeOrder}</a></td>
</tr>
</tbody>
</table>
{latestMessageId && <Button onClick={onCreateDraftClick}>Reply</Button>}
</div>
);
}
export default Tutorial;
```
---
## 🧭 Front Context Structure and SDK Methods Reference (with Types)
The Front `context` represents a partial state of Front that the user is interacting with. The context provides details about the selected conversation or selected conversations for sidebar plugins, and the selected conversation and current draft that the user is composing for composer plugins.
The SDK methods provide easy ways for plugin developers to perform Front-related actions like creating a draft message, listing teammates or inboxes, or adding a tag to a conversation. These methods should be used in the plugin code whenever possible to streamline development and programming paradigms.
The following code snippets detail the main Front `context` object and methods available.
contextTypesV2
```ts
import {ApplicationCancelToken} from './asyncTypesV2';
import {ApplicationChannelList} from './channelTypesV2';
import {ApplicationCommentList} from './commentTypesV2';
import {
ApplicationConversation,
ApplicationConversationStatus,
ApplicationSingleConversation,
} from './conversationTypesV2';
import {ApplicationDraft, ApplicationDraftTemplate, ApplicationDraftUpdate} from './draftTypesV2';
import {EntryPointData, EntryPointTypesEnum} from './entryPointTypesV2';
import {HttpRequest, HttpResponse} from './httpTypesV2';
import {
ApplicationAttachmentId,
ApplicationCommentId,
ApplicationConversationId,
ApplicationDraftId,
ApplicationInboxId,
ApplicationMessageId,
ApplicationTagId,
ApplicationTeammateId,
ApplicationTopicId,
} from './idTypesV2';
import {ApplicationInboxList} from './inboxTypesV2';
import {ApplicationLink} from './linkTypesV2';
import {ApplicationMessageList} from './messageTypesV2';
import {ApplicationPaginationToken} from './paginationTypesV2';
import {ApplicationRecipientList} from './recipientTypesV2';
import {HttpRelayRequest} from './relayTypesV2';
import {ApplicationTagList} from './tagTypesV2';
import {ApplicationTeammate, ApplicationTeammateList} from './teammateTypesV2';
import {ApplicationTicketStatusList} from './ticketStatusesTypesV2';
import {ApplicationTopic} from './topicTypesV2';
import {Widget, WidgetTemplate} from './widgetTypesV2';
export enum ApplicationAuthenticationStatusesEnum {
AUTHORIZED = 'authorized',
}
export interface ApplicationContextBase {
/** Unique ID for this context. */
id: string;
/** Unique ID of the Entry Point this context was created from. */
entryPointId: string;
/** Unique ID of the component that hosts this entry point. */
hostId: string;
/** List of function names supported by this context object with their arity. */
functionArities: {[K: string]: number};
/** Current teammate's preferences for this application. */
preferences: object;
/** Current teammate's authentication. */
authentication: {
status?: ApplicationAuthenticationStatusesEnum;
};
/** Current teammate. */
teammate: ApplicationTeammate;
/**
* Create a new Widget for the current context.
* @param template Definition of the Widget to create.
* @param cancelToken Cancel the creation of the Widget.
* @returns Newly created Widget.
*/
createWidget(template: WidgetTemplate, cancelToken?: ApplicationCancelToken): Promise<Widget>;
/**
* Destroy an existing Widget.
* @param widgetId ID of the Widget to destroy.
* @param cancelToken Cancel the Widget destruction.
*/
destroyWidget(widgetId: string, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Send an HTTP request to the private API of the application.
* @param request Definition of the HTTP request to run.
* @param cancelToken Cancel the HTTP request.
* @returns HTTP response.
*/
sendHttp(request: HttpRequest, cancelToken?: ApplicationCancelToken): Promise<HttpResponse>;
/**
* Relay an HTTP request.
* @param request Definition of the HTTP request to run.
* @param cancelToken Cancel the HTTP request.
* @returns HTTP response.
*/
relayHttp(request: HttpRelayRequest, cancelToken?: ApplicationCancelToken): Promise<HttpResponse>;
/**
* Start the authentication flow according to the configured security scheme.
*/
authenticate(cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Delete the credentials stored for the current teammate.
*/
deauthenticate(cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Open a browser window.
* @param url URL to navigate to.
*/
openUrl(url: string, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Open a browser popup.
* @param url URL to open.
* @param options Optional object to define the properties of the popup.
*/
openUrlInPopup(
url: string,
options: {width?: number; height?: number} | undefined,
cancelToken?: ApplicationCancelToken,
): Promise<void>;
/**
* Open a conversation in a popup.
* @param conversationId Conversation to open.
* @param cancelToken Cancel the request.
*/
openConversationInPopup(
conversationId: ApplicationConversationId,
cancelToken?: ApplicationCancelToken,
): Promise<void>;
/**
* Perform a search in Front for the current teammate.
* @param query Search query.
* @param cancelToken Cancel the search request.
*/
search(query: string, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* List the teammates of the company.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of teammates.
*/
listTeammates(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationTeammateList>;
/**
* List the inboxes accessible by the current teammate.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of inboxes.
*/
listInboxes(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationInboxList>;
/**
* List the channels accessible by the current teammate.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of channels.
*/
listChannels(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationChannelList>;
/**
* List the tags accessible by the current teammate.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of tags.
*/
listTags(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationTagList>;
/**
* List the ticket statuses accessible by the current teammate.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of ticket statuses.
*/
listTicketStatuses(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationTicketStatusList>;
/**
* Create a draft.
* @param template Properties of the draft to create
* @param cancelToken Cancel the request.
* @returns The newly created draft.
*/
createDraft(
template: ApplicationDraftTemplate,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationDraft>;
/**
* Update a draft.
* @param draftId ID of the draft to update.
* @param update Properties of the draft to update.
* @param cancelToken Cancel the request.
*/
updateDraft(
draftId: ApplicationDraftId,
update: ApplicationDraftUpdate,
cancelToken?: ApplicationCancelToken,
): Promise<void>;
/** Entry point of the application. */
entryPoint: EntryPointData | undefined;
}
export enum EntryPointNotificationTypesEnum {
INCOMING_CALL = 'INCOMING_CALL',
}
export interface IncomingCallContact {
name: string;
handle?: string;
}
export interface IncomingCallNotification {
type: EntryPointNotificationTypesEnum;
// Contact is optional because contact information may not be available.
contact?: IncomingCallContact;
}
export type EntryPointNotification = IncomingCallNotification;
interface NotificationContextBase {
/**
* Display a notification. This displays both an in-app banner and system notification.
* @param entryPointNotification Notification data.
* @param cancelToken Cancel the request.
* @returns The ID of the created notification.
* @beta
*/
displayNotification(
entryPointNotification: EntryPointNotification,
cancelToken?: ApplicationCancelToken,
): Promise<string>;
/**
* Dismiss a notification based on notification ID. This dismisses the in-app banner.
* A caveat is that desktop notifications cannot be dismissed.
* @param notificationId The notification ID.
* @param cancelToken Cancel the request.
* @returns void.
* @beta
*/
dismissNotification(notificationId: string, cancelToken?: ApplicationCancelToken): Promise<void>;
}
/*
* Background.
*/
export interface BackgroundContext extends ApplicationContextBase, NotificationContextBase {
type: 'background_mode';
}
/*
* Conversations.
*/
export interface NoConversationContext extends ApplicationContextBase, NotificationContextBase {
type: 'noConversation';
}
export interface ExistingConversationBaseContext extends ApplicationContextBase, NotificationContextBase {
/**
* Add a topic to the selected conversations.
* @param externalUrl URL of the topic.
* @param name Human readable name.
* @param cancelToken Cancel the creation of the link.
* @returns The added topic.
* @deprecated
*/
addTopic(
externalUrl: string,
name: string | undefined,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationTopic>;
/**
* Add a link to the selected conversations.
* @param externalUrl URL of the topic.
* @param name Human readable name.
* @param cancelToken Cancel the creation of the link.
* @returns The added link.
*/
addLink(
externalUrl: string,
name: string | undefined,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationLink>;
/**
* Assign or unassign the selected conversations.
* @param teammateId ID of the teammate to assign the conversations to. If null, the conversations will be unassigned.
* @param cancelToken Cancel the request.
*/
assign(teammateId: ApplicationTeammateId | null, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Move the selected conversations to an inbox.
* @param inboxId ID of the inbox.
* @param cancelToken Cancel the request.
*/
move(inboxId: ApplicationInboxId, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Set the status of the selected conversations.
* @param status Conversation status to apply.
* @param cancelToken Cancel the request.
*/
setStatus(status: ApplicationConversationStatus, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Add the specified tags to the selected conversations.
* @param tagIds Tag IDs to add.
* @param cancelToken Cancel the request.
*/
tag(tagIds: ReadonlyArray<ApplicationTagId>, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Remove the specified tags from the selected conversations.
* @param tagIds Tag IDs to remove.
* @param cancelToken Cancel the request.
*/
untag(tagIds: ReadonlyArray<ApplicationTagId>, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Remove the specified link from the conversation.
* @param linkId Link ID to remove.
* @param cancelToken Cancel the request.
*/
removeLink(linkId: ApplicationTopicId, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Fetch the current path the user is in within the app.
* @param cancelToken Cancel the request.
*/
fetchPath(cancelToken?: ApplicationCancelToken): Promise<string>;
}
export interface SingleConversationContext extends ExistingConversationBaseContext {
type: 'singleConversation';
/** The selected conversation. */
conversation: ApplicationSingleConversation;
/**
* Fetch a draft by ID.
* @param draftId ID of the draft to fetch.
* @param cancelToken Cancel the request.
* @returns The draft, if found.
*/
fetchDraft(
draftId: ApplicationDraftId,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationDraft | undefined>;
/**
* List the messages in the current conversation.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of messages.
*/
listMessages(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationMessageList>;
/**
* List the comments in the current conversation.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of comments.
*/
listComments(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationCommentList>;
/**
* Download the specified attachment that is attached to a message. To get attachments you will need to use
* {@link listMessages} which returns messages with any attachments.
* @param messageOrCommentId Message or comment id for the attachment.
* @param attachmentId Attachment id to be fetched.
* @param cancelToken Cancel the request.
* @returns File holding the data of the attachment
*/
downloadAttachment(
messageOrCommentId: ApplicationMessageId | ApplicationCommentId,
attachmentId: ApplicationAttachmentId,
cancelToken?: ApplicationCancelToken,
): Promise<File | undefined>;
/**
* List the recipients in the current conversation in the order in which the messages are sent.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of recipients.
*/
listRecipients(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationRecipientList>;
}
/**
* @deprecated
* This context has been deprecated and been replaced by the MessageComposerContext.
*/
export interface NoConversationPopoverContext
extends ApplicationContextBase,
Pick<SingleConversationContext, 'fetchDraft'> {
type: 'noConversationPopover';
draftId?: ApplicationDraftId;
/**
* Closes the popover.
* * @param cancelToken Cancel the request.
*/
requestClose(cancelToken?: ApplicationCancelToken): Promise<void>;
}
/**
* @deprecated
* This context has been deprecated and been replaced by the MessageComposerContext.
*/
export interface SingleConversationPopoverContext extends Omit<SingleConversationContext, 'type'> {
type: 'singleConversationPopover';
/**
* Closes the popover.
* * @param cancelToken Cancel the request.
*/
requestClose(cancelToken?: ApplicationCancelToken): Promise<void>;
}
export interface MultiConversationsContext extends ExistingConversationBaseContext {
type: 'multiConversations';
/** Selected conversations */
conversations: ReadonlyArray<ApplicationConversation>;
}
export type ExistingConversationContext = SingleConversationContext | MultiConversationsContext;
export type ConversationContext =
| NoConversationContext
| NoConversationPopoverContext
| SingleConversationContext
| SingleConversationPopoverContext
| MultiConversationsContext;
/*
* Message.
*/
export interface MessageContext extends ApplicationContextBase {
type: 'message';
messageId: string;
/**
* Fetch the current path the user is in within the app.
* @param cancelToken Cancel the request.
*/
fetchPath(cancelToken?: ApplicationCancelToken): Promise<string>;
}
/*
* Message composer.
*/
export interface MessageComposerContext extends ApplicationContextBase {
type: 'messageComposer';
draft: ApplicationDraft;
// The conversation key will always be defined even if the conversation does not exist.
/** The conversation will not exist for an unsaved (empty) draft. */
conversation: ApplicationSingleConversation | undefined;
/**
* Add a link to the conversation if it exists.
* @param externalUrl URL of the topic.
* @param name Human readable name.
* @param cancelToken Cancel the creation of the link.
* @returns The added link.
*/
addLink(
externalUrl: string,
name: string | undefined,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationLink>;
/**
* Assign or unassign the conversation if it exists.
* @param teammateId ID of the teammate to assign the conversations to. If null, the conversations will be unassigned.
* @param cancelToken Cancel the request.
*/
assign(teammateId: ApplicationTeammateId | null, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Move the conversation if it exists to an inbox.
* @param inboxId ID of the inbox.
* @param cancelToken Cancel the request.
*/
move(inboxId: ApplicationInboxId, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Set the status of the conversation if it exists.
* @param status Conversation status to apply.
* @param cancelToken Cancel the request.
*/
setStatus(status: ApplicationConversationStatus, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Add the specified tags to the conversation if it exists.
* @param tagIds Tag IDs to add.
* @param cancelToken Cancel the request.
*/
tag(tagIds: ReadonlyArray<ApplicationTagId>, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Remove the specified tags from the conversation if it exists.
* @param tagIds Tag IDs to remove.
* @param cancelToken Cancel the request.
*/
untag(tagIds: ReadonlyArray<ApplicationTagId>, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Remove the specified link from the conversation if it exists.
* @param linkId Link ID to remove.
* @param cancelToken Cancel the request.
*/
removeLink(linkId: ApplicationTopicId, cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* List the messages in the conversation if it exists.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of messages.
*/
listMessages(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationMessageList>;
/**
* List the comments in the conversation if it exists.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of comments.
*/
listComments(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationCommentList>;
/**
* List the recipients in the order in which the recipients are sent if the conversation exists.
* @param paginationToken Token of the requested page. If omitted, will return the first page of results.
* @param cancelToken Cancel the request.
* @returns List of recipients.
*/
listRecipients(
paginationToken?: ApplicationPaginationToken,
cancelToken?: ApplicationCancelToken,
): Promise<ApplicationRecipientList>;
/**
* Download the specified attachment that is attached to the draft.
* @param messageOrCommentId Message or comment id for the attachment.
* @param attachmentId Attachment id to be fetched.
* @param cancelToken Cancel the request.
* @returns File holding the data of the attachment
*/
downloadComposerAttachment(
attachmentId: ApplicationAttachmentId,
cancelToken?: ApplicationCancelToken,
): Promise<File | undefined>;
/**
* Close the message composer plugin.
*/
close(cancelToken?: ApplicationCancelToken): Promise<void>;
/**
* Close the draft and the composer plugin.
* @param cancelToken Cancel the request.
*/
closeDraft(cancelToken?: ApplicationCancelToken): Promise<void>;
}
/*
* Union type.
*/
export type ApplicationContext =
| ConversationContext
| MessageContext
| MessageComposerContext
| BackgroundContext;
/*
* Entry point => Context map.
*/
interface ContextMap {
[EntryPointTypesEnum.CONVERSATION_LINK_DROPDOWN]: ExistingConversationContext;
[EntryPointTypesEnum.MESSAGE_MORE_DROPDOWN]: MessageContext;
[EntryPointTypesEnum.SIDE_PANEL]: ConversationContext | BackgroundContext;
[EntryPointTypesEnum.URL]: ConversationContext;
[EntryPointTypesEnum.COMPOSER_TOOLBAR]: MessageComposerContext;
[EntryPointTypesEnum.CONTACT_HANDLE]: ConversationContext;
}
export type ContextOf<T extends keyof ContextMap> = ContextMap[T];
```
asyncTypesV2
```ts
import {ApplicationError} from './errorTypesV2';
export interface ApplicationCancel {
message: string;
}
export interface ApplicationCancelToken {
promise: Promise<ApplicationCancel>;
throwIfRequested(): void;
}
export type ApplicationCanceller = (message?: string) => void;
export interface ApplicationCancelTokenSource {
cancel: ApplicationCanceller;
token: ApplicationCancelToken;
}
export class ApplicationCancelError extends ApplicationError {}
export function buildApplicationCancelTokenSource(): ApplicationCancelTokenSource {
let resolve: ((cancel: ApplicationCancel) => void) | null = null;
let wasResolveCalled = false;
const promise = new Promise<ApplicationCancel>((promiseResolve) => {
resolve = promiseResolve;
});
const cancel: ApplicationCanceller = (message) => {
if (!resolve) {
throw new Error('Promise initialization failed');
}
wasResolveCalled = true;
resolve({message: message || 'The process was cancelled.'});
};
const throwIfRequested = () => {
if (wasResolveCalled) {
throw new ApplicationCancelError();
}
};
const token = {
promise,
throwIfRequested,
};
return {
cancel,
token,
};
}
```
paginationTypesV2
```ts
import {Branded} from 'io-ts';
/*
* Pagination token.
*/
export interface ApplicationPaginationTokenBrand {
readonly applicationPaginationToken: unique symbol;
}
export type ApplicationPaginationToken = Branded<string, ApplicationPaginationTokenBrand>;
/*
* Paginated results.
*/
export interface ApplicationPaginatedResults<T> {
/** List of results. */
results: ReadonlyArray<T>;
/** Token to access the next page of results, if any. */
nextPageToken: ApplicationPaginationToken | undefined;
}
```
recipientTypesV2
```ts
import {ApplicationContact} from './contactTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export interface ApplicationRecipient {
/** Handle of the recipient. */
handle: string;
/** Name of the recipient. */
name: string | undefined;
/** The corresponding contact, if any. */
contact: ApplicationContact | undefined;
/** The type of the handle */
type: string;
}
export type ApplicationRecipientList = ApplicationPaginatedResults<ApplicationRecipient>;
```
relayTypesV2
```ts
import {HttpVerbsEnum} from './httpTypesV2';
export enum RelayAuthenticationSchemesEnum {
BEARER = 'bearer',
OAUTH2 = 'oauth2',
NONE = 'none',
}
export interface HttpRelayRequest {
/** The HTTP verb to use for the request. */
verb: HttpVerbsEnum;
/** The path relative to this application. */
url: string;
/** HTTP headers to relay with the request. */
headers?: Record<string, string>;
/** The body to send. */
body?: unknown;
}
```
tagTypesV2
```ts
import {ApplicationCompanyId, ApplicationTagId, ApplicationTeamId, ApplicationTeammateId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export interface ApplicationTag {
/** Unique ID of the Tag. */
id: ApplicationTagId;
/** ID of the team or teammate the tag belongs to. */
ownerId: ApplicationTeamId | ApplicationTeammateId | ApplicationCompanyId;
/** Name of the tag. */
name: string;
/** Parent tag, if any. */
parentTag: ApplicationTag | undefined;
}
export type ApplicationTagList = ApplicationPaginatedResults<ApplicationTag>;
```
teammateTypesV2
```ts
import {ApplicationTeammateId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export interface ApplicationTeammate {
/** Unique ID of the teammate. */
id: ApplicationTeammateId;
/** Name of the teammate. */
name: string;
/** Username of the teammate, used to @-mention them in a comment. */
username: string;
/** Email address of the teammate’s account. */
email: string;
}
export type ApplicationTeammateList = ApplicationPaginatedResults<ApplicationTeammate>;
```
ticketStatusTypesV2
```ts
import {ApplicationConversationStatusCategory} from './conversationTypesV2';
import {ApplicationTicketStatusId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export interface ApplicationTicketStatus {
/** Unique ID of the ticket status. */
id: ApplicationTicketStatusId;
/** Name of the ticket status. */
name: string;
/** Category of the ticket status. */
category: ApplicationConversationStatusCategory;
/** Description of the ticket status. */
description?: string;
/** Timestamp when the ticket status was last updated. */
updatedAt: number;
/** Timestamp when the ticket status was created. */
createdAt: number;
}
export type ApplicationTicketStatusList = ApplicationPaginatedResults<ApplicationTicketStatus>;
```
topicTypesV2
```ts
import {ApplicationTopicId} from './idTypesV2';
export interface ApplicationTopic {
/** ID of the topic. */
id: ApplicationTopicId;
/** Type of the topic. */
type: 'web'; // Temporary until we support app-specific topic types.
/** Human readable name. */
name: string | undefined;
/** URL of the topic. */
externalUrl: string;
/** URL to open the list of conversations related to the topic in Front. */
appUrl: string;
}
```
widgetTypesV2
```ts
/*
* Widget types.
*/
export enum WidgetTypesEnum {
BLOCK = 'BLOCK',
LAYER = 'LAYER',
}
/*
* Template.
*/
export interface WidgetTemplate {
id: string;
type: WidgetTypesEnum;
}
/*
* Widget.
*/
export interface Widget {
id: string;
type: WidgetTypesEnum;
window: Window;
}
```
attachmentTypesV2
```ts
import {ApplicationAttachmentId} from './idTypesV2';
/*
* Attachment.
*/
export interface ApplicationAttachment {
/** Unique ID of the attachment. */
id: ApplicationAttachmentId;
/** Name of the file. */
name: string;
/** MIME type of the file. */
contentType: string;
/** Size of the file (in bytes). */
size: number;
/** Identifier of the file in the body, if inline. */
inlineCid: string | undefined;
}
export interface ApplicationDraftAttachment extends Omit<ApplicationAttachment, 'id'> {
/**
* Unique ID of the attachment.
* This may be undefined if the draft has not been saved yet.
*/
id: ApplicationAttachmentId | undefined;
}
```
channelTypesV2
```ts
import {ApplicationChannelId} from './idTypesV2';
import {ApplicationInbox} from './inboxTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export type ApplicationChannelType =
| 'custom'
| 'aircall'
| 'aircall_sms'
| 'dialpad'
| 'dialpad_sms'
| 'email'
| 'facebook'
| 'frontForm'
| 'frontChat'
| 'portal'
| 'googlePlay'
| 'intercom'
| 'smooch'
| 'solutions_by_text_sms'
| 'talkdesk'
| 'truly'
| 'twilio'
| 'twitter'
| 'twitterDm'
| 'whatsapp';
export interface ApplicationChannel {
/** Unique ID of the Channel. */
id: ApplicationChannelId;
/** Type of the channel. */
type: ApplicationChannelType;
/** Inbox this channel belongs to. */
inbox: ApplicationInbox;
/** Name of the Channel. */
name: string;
/** Address used to send messages. */
address: string;
}
export type ApplicationChannelList = ApplicationPaginatedResults<ApplicationChannel>;
```
commentTypesV2
```ts
import {ApplicationAttachment} from './attachmentTypesV2';
import {ApplicationCommentId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
import {ApplicationTeammate} from './teammateTypesV2';
/*
* Comment.
*/
export interface ApplicationComment {
/** Unique ID of the comment. */
id: ApplicationCommentId;
/** Teammate author of the comment. */
author: ApplicationTeammate;
/** Content properties of the message. */
content: ApplicationCommentContent;
/** When the comment was created */
createdAt: Date;
/** When the comment was last edited */
lastEditedAt?: Date;
}
export interface ApplicationCommentContent {
/** Content of the message in markdown format. */
body: string;
/** List of files attached to the message. */
attachments: ReadonlyArray<ApplicationAttachment>;
}
export type ApplicationCommentList = ApplicationPaginatedResults<ApplicationComment>;
```
contactTypesV2
```ts
import {
ApplicationContactGroupId,
ApplicationContactId,
ApplicationCustomFieldId,
ApplicationInboxId,
ApplicationTeammateId,
} from './idTypesV2';
/*
* Contact.
*/
export interface ApplicationContact {
/** Unique ID of the Contact. */
id: ApplicationContactId;
/** Name of the contact. */
name: string | undefined;
/** Description of the contact. */
description: string | undefined;
/** List of the contact's handles. */
handles: ReadonlyArray<ApplicationContactHandle>;
/** List of groups the contact belongs to. */
groupIds: ReadonlyArray<ApplicationContactGroupId>;
/** List of the contact's custom attributes. */
customAttributes: ReadonlyArray<ApplicationCustomAttribute>;
}
/*
* Contact handle.
*/
export type ApplicationContactHandleType =
| 'email'
| 'phone'
| 'twitter'
| 'custom'
| 'facebook'
| 'intercom'
| 'smooch'
| 'frontChat'
| 'whatsapp'
| 'googlePlay';
interface ApplicationContactHandle {
/** Type of the handle. */
type: ApplicationContactHandleType;
/** Handle used to exchange messages with the contact. */
handle: string;
}
/*
* Contact custom field.
*/
interface ApplicationCustomFieldTypeMap {
string: string;
enum: string;
datetime: Date;
boolean: boolean;
number: number;
teammateId: ApplicationTeammateId;
inboxId: ApplicationInboxId;
unknown: string;
}
interface ApplicationCustomAttributeBase<T extends keyof ApplicationCustomFieldTypeMap> {
id: ApplicationCustomFieldId;
name: string;
type: T;
value: ApplicationCustomFieldTypeMap[T];
}
export type ApplicationCustomAttribute =
| ApplicationCustomAttributeBase<'string'>
| ApplicationCustomAttributeBase<'enum'>
| ApplicationCustomAttributeBase<'datetime'>
| ApplicationCustomAttributeBase<'boolean'>
| ApplicationCustomAttributeBase<'number'>
| ApplicationCustomAttributeBase<'teammateId'>
| ApplicationCustomAttributeBase<'inboxId'>
| ApplicationCustomAttributeBase<'unknown'>;
```
conversationTypesV2
```ts
import {ApplicationCustomAttribute} from './contactTypesV2';
import {ApplicationConversationId, ApplicationDraftId} from './idTypesV2';
import {ApplicationInbox} from './inboxTypesV2';
import {ApplicationLink} from './linkTypesV2';
import {ApplicationRecipient} from './recipientTypesV2';
import {ApplicationTag} from './tagTypesV2';
import {ApplicationTeammate} from './teammateTypesV2';
import {ApplicationTopic} from './topicTypesV2';
export type ApplicationConversationType =
| 'email'
| 'whatsapp'
| 'frontChat'
| 'internal'
| 'phoneCall'
| 'tweet'
| 'twitterDm'
| 'sms'
| 'googlePlay'
| 'intercom'
| 'smooch'
| 'facebook'
| 'custom';
export type ApplicationConversationStatus = 'open' | 'archived' | 'trashed' | 'spam';
export type ApplicationConversationStatusCategory = 'open' | 'waiting' | 'resolved';
export interface ApplicationConversation {
/** Unique ID of the conversation. */
id: ApplicationConversationId;
/** Type of messages in the conversation. */
type: ApplicationConversationType;
/** Status of the conversation for the current user. */
status: ApplicationConversationStatus;
/** Subject. */
subject: string | undefined;
/** Blurb of the latest message in the conversation. */
blurb: string | undefined;
/** Teammate assigned to the conversation, if any. */
assignee: ApplicationTeammate | undefined;
/** Main recipient of the conversation. */
recipient: ApplicationRecipient | undefined;
/** Inboxes the conversation is in. */
inboxes: ReadonlyArray<ApplicationInbox>;
/** Tags attached to the conversation. */
tags: ReadonlyArray<ApplicationTag>;
/** @deprecated Topics attached to the conversation. */
topics: ReadonlyArray<ApplicationTopic>;
/** Links attached to the conversation. */
links: ReadonlyArray<ApplicationLink>;
/** The time when conversation was closed. */
closedAt: number | undefined;
/** Unique ID of the conversation status category, only present if ticketing is enabled */
statusId: string | undefined;
/** Status category of the conversation */
statusCategory: ApplicationConversationStatusCategory | undefined;
/** List of ticket ids associated with the conversation */
ticketIds: ReadonlyArray<string>;
/** Custom field attributes associated with the conversation */
customFieldAttributes: ReadonlyArray<ApplicationCustomAttribute>;
}
export interface ApplicationSingleConversation extends ApplicationConversation {
/** ID of the draft message on this conversation, if any. */
draftId: ApplicationDraftId | undefined;
}
```
draftTypesV2
```ts
import {ApplicationDraftAttachment} from './attachmentTypesV2';
import {ApplicationChannel} from './channelTypesV2';
import {ApplicationChannelId, ApplicationDraftId, ApplicationMessageId} from './idTypesV2';
import {ApplicationRecipient} from './recipientTypesV2';
export type ApplicationDraftReplyType = 'forward' | 'reply' | 'replyAll';
export interface ApplicationDraftContent {
/** Content of the message. */
body: string;
/** Content type of the body. */
type: 'html' | 'text';
}
export interface ApplicationDraft {
/** Unique ID of the draft. */
id: ApplicationDraftId;
/** Channel the draft will be sent from. */
channel: ApplicationChannel;
/** List of recipients the message is addressed to. */
to: ReadonlyArray<ApplicationRecipient>;
/** List of recipients in copy of the emssage. */
cc: ReadonlyArray<ApplicationRecipient> | undefined;
/** List of recipients in blind copy of the message. */
bcc: ReadonlyArray<ApplicationRecipient> | undefined;
/** Subject of the message. */
subject: string | undefined;
/** Content properties of the draft. */
content: ApplicationDraftContent & {
attachments: ReadonlyArray<ApplicationDraftAttachment>;
};
/** Whether the draft is editable */
isEditable: boolean;
}
interface ApplicationDraftTemplateBase {
/** Handles to send the message to. */
to?: ReadonlyArray<string>;
/** Handles to add in copy of the message. Supported only for email channels. */
cc?: ReadonlyArray<string>;
/** Handles to add in blind copy of the message. Supported only for email channels. */
bcc?: ReadonlyArray<string>;
/** Subject of the message. */
subject?: string;
}
export interface ApplicationDraftTemplate extends ApplicationDraftTemplateBase {
/** ID of the channel to use to compose the message. */
channelId?: ApplicationChannelId;
/** Option to create a draft from another message. */
replyOptions?: {
/** Type of the reply */
type: ApplicationDraftReplyType;
/** ID of the message to reply to or forward. */
originalMessageId: ApplicationMessageId;
};
/** Content properties of the draft. */
content?: ApplicationDraftContent;
/** Attachments to be added to draft */
attachments?: ReadonlyArray<File>;
}
export interface ApplicationDraftUpdate extends ApplicationDraftTemplateBase {
/** Logic to apply the update. */
updateMode: 'insert' | 'replace';
/** Content properties of the draft. */
content?: ApplicationDraftContent;
/** Attachments for the draft */
attachments?: ReadonlyArray<File>;
}
```
entryPointTypesV2
```ts
/*
* Entry point types.
*/
export enum EntryPointTypesEnum {
CONVERSATION_LINK_DROPDOWN = 'CONVERSATION_LINK_DROPDOWN',
MESSAGE_MORE_DROPDOWN = 'MESSAGE_MORE_DROPDOWN',
SIDE_PANEL = 'SIDE_PANEL',
URL = 'URL',
COMPOSER_TOOLBAR = 'COMPOSER_TOOLBAR',
CONTACT_HANDLE = 'CONTACT_HANDLE',
}
/*
* Entry point template.
*/
export interface EntryPointTemplate {
id: string;
type: EntryPointTypesEnum;
}
/*
* Entry point data types.
*/
export enum EntryPointDataTypesEnum {
PHONE = 'phone',
}
interface PhoneEntryPointData {
type: EntryPointDataTypesEnum.PHONE;
handle: string;
timestamp?: number;
}
interface ContactHandleEntryPointData {
type: EntryPointTypesEnum.CONTACT_HANDLE;
data: PhoneEntryPointData;
}
interface SidePanelEntryPointData {
type: EntryPointTypesEnum.SIDE_PANEL;
}
interface SidePanelViaUrlEntryPointData {
type: EntryPointTypesEnum.URL;
data: {customContextProperties: Record<string, string>};
}
interface ComposerToolbarEntryPointData {
type: EntryPointTypesEnum.COMPOSER_TOOLBAR;
}
export type EntryPointData =
| ContactHandleEntryPointData
| SidePanelEntryPointData
| SidePanelViaUrlEntryPointData
| ComposerToolbarEntryPointData;
```
errorTypesV2
```ts
export class ApplicationError extends Error {
constructor(message?: string) {
// "Error" breaks the prototype chain here.
super(message);
// We need to restore it.
const actualPrototype = new.target.prototype;
Object.setPrototypeOf(this, actualPrototype);
}
}
export class ApplicationDefaultError extends ApplicationError {
constructor(error: Error) {
super('Something went wrong.');
this.originalError = error;
}
readonly originalError: Error;
}
```
httpTypesV2
```ts
export enum HttpVerbsEnum {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
DELETE = 'DELETE',
}
export interface HttpRequest {
/** The HTTP verb to use for the request. */
verb: HttpVerbsEnum;
/** The path relative to this application. */
path: string;
/** The body to send. */
body?: unknown;
}
export interface HttpResponse {
/** HTTP status code */
status: number;
/** Headers of the response */
headers: Record<string, string | undefined>;
/** Body of the response. */
body: unknown;
}
```
idTypesV2
```ts
import {Branded} from 'io-ts';
/*
* Brand types.
*/
interface ApplicationActivityIdBrand {
readonly publicIdActivity: unique symbol;
}
interface ApplicationAttachmentIdBrand {
readonly publicIdAttachment: unique symbol;
}
interface ApplicationCalendarEventIdBrand {
readonly publicIdCalendarEvent: unique symbol;
}
interface ApplicationCalendarEventVersionIdBrand {
readonly publicIdCalendarEventVersion: unique symbol;
}
interface ApplicationCannedAnswerIdBrand {
readonly publicIdCannedAnswer: unique symbol;
}
interface ApplicationCardIdBrand {
readonly publicIdCard: unique symbol;
}
interface ApplicationCardGroupIdBrand {
readonly publicIdCardGroup: unique symbol;
}
interface ApplicationChannelIdBrand {
readonly publicIdChannel: unique symbol;
}
interface ApplicationCommentIdBrand {
readonly publicIdComment: unique symbol;
}
interface ApplicationConversationIdBrand {
readonly publicIdConversation: unique symbol;
}
interface ApplicationCustomFieldIdBrand {
readonly publicIdCustomField: unique symbol;
}
interface ApplicationDraftIdBrand {
readonly publicUidDraft: unique symbol;
}
interface ApplicationInboxIdBrand {
readonly publicIdInbox: unique symbol;
}
interface ApplicationMessageIdBrand {
readonly publicIdMessage: unique symbol;
}
interface ApplicationRuleIdBrand {
readonly publicIdRule: unique symbol;
}
interface ApplicationTagIdBrand {
readonly publicIdTag: unique symbol;
}
interface ApplicationTicketStatusIdBrand {
readonly publicIdTicketStatusTag: unique symbol;
}
interface ApplicationTeamIdBrand {
readonly publicIdTeam: unique symbol;
}
interface ApplicationTeammateIdBrand {
readonly publicIdTeammate: unique symbol;
}
interface ApplicationTopicIdBrand {
readonly publicIdTopic: unique symbol;
}
interface ApplicationCircleIdBrand {
readonly publicIdCircle: unique symbol;
}
export type ApplicationResourceIdBrand = ApplicationActivityIdBrand &
ApplicationAttachmentIdBrand &
ApplicationCalendarEventIdBrand &
ApplicationCalendarEventVersionIdBrand &
ApplicationCannedAnswerIdBrand &
ApplicationCardIdBrand &
ApplicationCardGroupIdBrand &
ApplicationChannelIdBrand &
ApplicationCircleIdBrand &
ApplicationCommentIdBrand &
ApplicationConversationIdBrand &
ApplicationCustomFieldIdBrand &
ApplicationDraftIdBrand &
ApplicationInboxIdBrand &
ApplicationMessageIdBrand &
ApplicationRuleIdBrand &
ApplicationTagIdBrand &
ApplicationTicketStatusIdBrand &
ApplicationTeamIdBrand &
ApplicationTeammateIdBrand &
ApplicationTopicIdBrand;
/*
* Public ID types.
*/
export type ApplicationAttachmentId = Branded<string, ApplicationAttachmentIdBrand>;
export type ApplicationCannedAnswerId = Branded<string, ApplicationCannedAnswerIdBrand>;
export type ApplicationChannelId = Branded<string, ApplicationChannelIdBrand>;
export type ApplicationContactId = Branded<string, ApplicationCardIdBrand>;
export type ApplicationContactGroupId = Branded<string, ApplicationCardGroupIdBrand>;
export type ApplicationCommentId = Branded<string, ApplicationCommentIdBrand>;
export type ApplicationConversationId = Branded<string, ApplicationConversationIdBrand>;
export type ApplicationCustomFieldId = Branded<string, ApplicationCustomFieldIdBrand>;
export type ApplicationDraftId = Branded<string, ApplicationDraftIdBrand>;
export type ApplicationInboxId = Branded<string, ApplicationInboxIdBrand>;
export type ApplicationMessageId = Branded<string, ApplicationMessageIdBrand>;
export type ApplicationTagId = Branded<string, ApplicationTagIdBrand>;
export type ApplicationTicketStatusId = Branded<string, ApplicationTicketStatusIdBrand>;
export type ApplicationTeamId = Branded<string, ApplicationTeamIdBrand>;
export type ApplicationTeammateId = Branded<string, ApplicationTeammateIdBrand>;
export type ApplicationTopicId = Branded<string, ApplicationTopicIdBrand>;
export const applicationCompanyId = 'company';
export type ApplicationCompanyId = typeof applicationCompanyId;
```
inboxTypesV2
```ts
import {ApplicationInboxId, ApplicationTeamId, ApplicationTeammateId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
export interface ApplicationInbox {
/** Unique ID of the Inbox. */
id: ApplicationInboxId;
/** ID of the team or teammate owning the inbox. */
ownerId: ApplicationTeamId | ApplicationTeammateId;
/** Name of the inbox. */
name: string;
}
export type ApplicationInboxList = ApplicationPaginatedResults<ApplicationInbox>;
```
linkTypesV2
```ts
import {ApplicationTopicId} from './idTypesV2';
export interface ApplicationLink {
/** ID of the link. */
id: ApplicationTopicId;
/** Type of the topic. */
type: 'web'; // Temporary until we support app-specific topic types.
/** Human readable name. */
name: string | undefined;
/** URL of the topic. */
externalUrl: string;
/** URL to open the list of conversations related to the topic in Front. */
appUrl: string;
}
```
messageTypesV2
```ts
import {ApplicationAttachment} from './attachmentTypesV2';
import {ApplicationMessageId} from './idTypesV2';
import {ApplicationPaginatedResults} from './paginationTypesV2';
import {ApplicationRecipient} from './recipientTypesV2';
/*
* Message.
*/
export type ApplicationMessageStatus = 'inbound' | 'outbound';
export interface ApplicationMessage extends ApplicationMessageRecipients {
/** Unique ID of the message. */
id: ApplicationMessageId;
/** Subject of the message. */
subject: string | undefined;
/** Date at which the message has been sent or received. Can be undefined if the message is a draft. */
date: Date;
/** Status of the message. */
status: ApplicationMessageStatus;
/** Content properties of the message. Will be undefined if the content is not available. */
content: ApplicationMessageContent | undefined;
}
export interface ApplicationMessageRecipients {
/** Recipient the message is coming from. */
from: ApplicationRecipient;
/** Recipient to use to reply to this message. */
replyTo: ApplicationRecipient | undefined;
/** List of recipients the message is addressed to. */
to: ReadonlyArray<ApplicationRecipient>;
/** List of recipients in copy of the message. */
cc: ReadonlyArray<ApplicationRecipient> | undefined;
/** List of recipients in blind copy of the message. */
bcc: ReadonlyArray<ApplicationRecipient> | undefined;
}
export interface ApplicationMessageContent {
/** Content of the message. */
body: string;
/** Content type of the body. */
type: 'html' | 'text';
/** List of files attached to the message. */
attachments: ReadonlyArray<ApplicationAttachment>;
}
export type ApplicationMessageList = ApplicationPaginatedResults<ApplicationMessage>;
```
### List of SDK method/function examples
Here is a list of the SDK methods/functions as exported examples:
```ts
const draftId = 'dra:d0d88648770ef84f0da0b9c9df0f746e';
const messageId = '';
export const conversationContextFunctions = [
{title: 'Search', name: 'search', args: ['https://example.com']},
{title: 'Open external URL', name: 'openUrl', args: ['https://example.com']},
{
title: 'Open external URL in popup',
name: 'openUrlInPopup',
args: ['https://example.com', {width: 100, height: 100}]
},
{title: 'Open internal URL', name: 'openUrl', args: ['/open/msg_hslh0ar']},
{title: 'List tags', name: 'listTags', args: []},
{title: 'List teammates', name: 'listTeammates', args: []},
{title: 'List inboxes', name: 'listInboxes', args: []},
{title: 'List channels', name: 'listChannels', args: []},
{title: 'List messages', name: 'listMessages', args: []},
{title: 'List recipients', name: 'listRecipients', args: []},
{title: 'Fetch draft', name: 'fetchDraft', args: [draftId]},
{title: 'Add topic', name: 'addTopic', args: ['https://example.com']},
{title: 'Add link', name: 'addLink', args: ['https://example.com']},
{title: 'Assign', name: 'assign', args: ['tea_1']},
{title: 'Unassign', name: 'assign', args: [null]},
{title: 'Move', name: 'move', args: ['inb_1']},
{title: 'Archive', name: 'setStatus', args: ['archived']},
{title: 'Reopen', name: 'setStatus', args: ['open']},
{title: 'Trash', name: 'setStatus', args: ['trashed']},
{title: 'Mark as spam', name: 'setStatus', args: ['spam']},
{title: 'Tag', name: 'tag', args: [['tag_9']]},
{title: 'Untag', name: 'untag', args: [['tag_9']]},
{title: 'Remove Link', name: 'removeLink', args: ['top_1']},
{title: 'Open conversation in popup', name: 'openConversationInPopup', args: ['cnv_oc53wb2']},
{
title: 'Compose new message (HTML)',
name: 'createDraft',
args: [
{
channelId: 'cha_1',
subject: 'Hello world!',
to: ['[email protected]', '[email protected]'],
cc: ['[email protected]'],
content: {
body: `Hello,<br><br> <strong>World</strong>!
\\o/`,
type: 'html'
}
}
]
},
{
title: 'Compose new message (text)',
name: 'createDraft',
args: [
{
channelId: 'cha_1',
subject: 'Hello world!',
to: ['[email protected]'],
content: {
body: `Hello,\n\n**World**!
1. One.
2. Two.
\\o/`,
type: 'text'
}
}
]
},
{
title: 'Reply',
name: 'createDraft',
args: [
{
channelId: 'cha_1',
subject: 'Hello world!',
to: ['[email protected]', '[email protected]'],
cc: ['[email protected]'],
content: {body: 'Hello, reply!', type: 'text'},
replyOptions: {type: 'reply', originalMessageId: messageId}
}
]
},
{
title: 'Reply All',
name: 'createDraft',
args: [
{
channelId: 'cha_1',
subject: 'Hello world!',
to: ['[email protected]', '[email protected]'],
cc: ['[email protected]'],
content: {body: 'Hello, reply all!', type: 'text'},
replyOptions: {type: 'replyAll', originalMessageId: messageId}
}
]
},
{
title: 'Forward',
name: 'createDraft',
args: [
{
channelId: 'cha_1',
subject: 'Hello world!',
to: ['[email protected]', '[email protected]'],
cc: ['[email protected]'],
content: {body: 'Hello, forward!', type: 'text'},
replyOptions: {type: 'forward', originalMessageId: messageId}
}
]
},
{
title: 'Update draft (replace HTML)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello updated world!',
to: [],
cc: ['[email protected]'],
updateMode: 'replace',
content: {
body: 'Hello,<br><br> this is <strong>amazing</strong>!',
type: 'html'
}
}
]
},
{
title: 'Update draft (insert HTML)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello!',
to: [],
cc: ['[email protected]'],
updateMode: 'insert',
content: {
body: '<br>Insert <strong>HTML</strong><br>',
type: 'html'
}
}
]
},
{
title: 'Update draft (replace text)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello updated world!',
to: [],
cc: ['[email protected]'],
updateMode: 'replace',
content: {
body: 'Hello, replaced body!',
type: 'text'
}
}
]
},
{
title: 'Update draft (insert text)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello!',
to: ['[email protected]'],
cc: [],
updateMode: 'insert',
content: {
body: 'Insert!\n',
type: 'text'
}
}
]
}
];
export const messageComposerContextFunctions = [
{title: 'Search', name: 'search', args: ['https://example.com']},
{title: 'Open external URL', name: 'openUrl', args: ['https://example.com']},
{
title: 'Open external URL in popup',
name: 'openUrlInPopup',
args: ['https://example.com', {width: 100, height: 100}]
},
{title: 'Open internal URL', name: 'openUrl', args: ['/open/msg_hslh0ar']},
{title: 'List tags', name: 'listTags', args: []},
{title: 'List teammates', name: 'listTeammates', args: []},
{title: 'List inboxes', name: 'listInboxes', args: []},
{title: 'List channels', name: 'listChannels', args: []},
{title: 'List messages', name: 'listMessages', args: []},
{title: 'List recipients', name: 'listRecipients', args: []},
{title: 'List comments', name: 'listComments', args: []},
{title: 'Add link', name: 'addLink', args: ['https://example.com']},
{title: 'Assign', name: 'assign', args: ['tea_1']},
{title: 'Unassign', name: 'assign', args: [null]},
{title: 'Move', name: 'move', args: ['inb_1']},
{title: 'Archive', name: 'setStatus', args: ['archived']},
{title: 'Reopen', name: 'setStatus', args: ['open']},
{title: 'Trash', name: 'setStatus', args: ['trashed']},
{title: 'Mark as spam', name: 'setStatus', args: ['spam']},
{title: 'Tag', name: 'tag', args: [['tag_9']]},
{title: 'Untag', name: 'untag', args: [['tag_9']]},
{title: 'Remove Link', name: 'removeLink', args: ['top_1']},
{title: 'Open conversation in popup', name: 'openConversationInPopup', args: ['cnv_oc53wb2']},
{title: 'Download composer attachment', name: `downloadComposerAttachment`, args: []},
{
title: 'Update draft (replace HTML)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello updated world!',
to: [],
cc: ['[email protected]'],
updateMode: 'replace',
content: {
body: 'Hello,<br><br> this is <strong>amazing</strong>!',
type: 'html'
}
}
]
},
{
title: 'Update draft (insert HTML)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello!',
to: [],
cc: ['[email protected]'],
updateMode: 'insert',
content: {
body: '<br>Insert <strong>HTML</strong><br>',
type: 'html'
}
}
]
},
{
title: 'Update draft (replace text)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello updated world!',
to: [],
cc: ['[email protected]'],
updateMode: 'replace',
content: {
body: 'Hello, replaced body!',
type: 'text'
}
}
]
},
{
title: 'Update draft (insert text)',
name: 'updateDraft',
args: [
draftId,
{
subject: 'Hello!',
to: ['[email protected]'],
cc: [],
updateMode: 'insert',
content: {
body: 'Insert!\n',
type: 'text'
}
}
]
}
];
```
---
Updated about 8 hours ago