Freshdesk
The Freshdesk integration allows you to create, update, and delete tickets; reply to and create notes for tickets as well as view Groups, Agents and Contacts.
Case Management · Freshdesk
Details
| ID | Freshdesk |
|---|---|
| Provider | Freshworks Inc. |
| Category | Case Management |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Use the Freshdesk integration to manage and create Freshdesk tickets from Cortex XSOAR.
Use Cases
- Create a Freshdesk ticket from Cortex XSOAR
- Update a Freshdesk ticket from Cortex XSOAR
- Get information from a Freshdesk ticket
Configure Freshdesk on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Freshdesk.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Server URL https://demistohelp.freshdesk.com )
- API Token. (You must enter either the API token or your Freshdesk credentials)
- Trust any certificate (not secure)
- Use system proxy
- First fetch timestamp ( , e.g., 12 hours, 7 days)
- Username
- Click Test to validate the URLs, token, and connection.
Commands
You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
- Create a ticket: fd-create-ticket
- Get ticket details: fd-get-ticket
- Update a ticket: fd-update-ticket
- Get a list of all contacts: fd-list-contacts
- Get contact details: fd-get-contact
- Get a list of all canned response folders: fd-list-canned-response-folders
- Get a list of details for all canned responses in a folder: fd-get-canned-response-folder
- Get a list of all groups: fd-list-groups
- Add a reply to a ticket: fd-ticket-reply
- Get a list of all replies and notes for a ticket: fd-get-ticket-conversations
- Get a list of all agents: fd-list-agents
- Create a note for a ticket: fd-create-ticket-note
- Delete a ticket: fd-delete-ticket
- Search tickets: fd-search-tickets
1. Create a ticket
Creates a new Freshdesk ticket.
Base Command
fd-create-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| subject | Subject of the ticket | Required |
| description | Details of the issue that you are creating a ticket for | Required |
| priority | Priority of the ticket. Each number has a corresponding value. 1 - Low, 2 - Medium, 3 - High, 4 - Urgent. This argument accepts the priority number or string. |
Optional |
| status | Status of the ticket. Each number has a corresponding value. 2 - Open, 3 - Pending, 4 - Resolved, 5 - Closed, 6 - Waiting on Customer, 7 - Waiting on Third Party. This argument accepts the ticket status number or string. |
Optional |
| identifier | Email address or a Twitter handle of the requesting user. If an email address is entered and no contact exists with this email address in Freshdesk, it will be added as a new contact. If a Twitter handle is entered and no contact exists with this handle in Freshdesk, it will be added as a new contact. |
Required |
| responder | ID or name of the group or agent to assign the ticket to. Use the fd-list-groups command to find potential assignees. |
Optional |
| attachments | CSV list of entry IDs of files to attach to the ticket. For example: “468@73f988d1-bda2-4adc-8e02-926f02190070,560@73f988d1-bda2-4adc-8e02-926f02190070”. The total size of these attachments cannot exceed 15MB. |
Optional |
| additional_fields | Additional, optional ticket fields. Format - “field=value” where field value pairs are delimited from subsequent pairs by a semicolon symbol ‘;’ and where values that are lists are delimited by commas ‘,’. For example: “name=Jeffrey Collins;email=jeffrey.collins@gmail.com;tags=new,attention needed,billing related” |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.ID | Number | ID number of the ticket |
| Freshdesk.Ticket.Priority | Number | Ticket priority |
| Freshdesk.Ticket.DueBy | Date | Timestamp that denotes when the ticket is due to be resolved |
| Freshdesk.Ticket.Subject | String | Ticket subject |
| Freshdesk.Ticket.Status | Number | Status of the ticket |
| Freshdesk.Ticket.RequesterID | Number | User ID of the requester |
| Freshdesk.Ticket.Tag | Unknown | Tags associated with the ticket |
| Freshdesk.Ticket.GroupID | Number | ID of the group the ticket was assigned to |
| Freshdesk.Ticket.Source | Number | The channel through which the ticket was created |
| Freshdesk.Ticket.CreatedAt | Date | Ticket creation timestamp |
| Freshdesk.Ticket.ResponderID | Number | ID of the agent the ticket was assigned to |
| Freshdesk.Ticket.FrDueBy | Date | Timestamp that denotes when the first response is due |
| Freshdesk.Ticket.AdditionalFields | Unknown | Additional fields and values that were entered using the ‘additional_fields’ arguments |
| Freshdesk.Ticket.Attachment.AttachmentURL | String | URL to download the file attached to the ticket to your local machine |
| Freshdesk.Ticket.Attachment.Name | Unknown | The name of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ID | Unknown | ID number for the file attached to the ticket |
| Freshdesk.Ticket.Attachment.Size | String | Size of the file attached to the ticket |
Command Example
!fd-create-ticket subject="Demonstrate Ticket Creation" description="<em>Here</em> we are demonstrating the freshdesk integration ticket creation command" identifier="jeffrey.collins@gmail.com" priority="High" additional_fields="name=Jeff Testman;email=jeffrey.collins@gmail.com;tags=new,attention needed,billing related"
Context Example
{
"Freshdesk.Ticket": {
"Status": 2,
"DueBy": "2019-02-07T09:00:00Z",
"FrDueBy": "2019-02-06T10:00:00Z",
"Priority": 3,
"Source": 2,
"Tag": [
"attention needed",
"billing related",
"new"
],
"RequesterID": 2043024010476,
"UpdatedAt": "2019-02-05T15:55:35Z",
"AdditionalFields": {
"DescriptionHTML": "<em>Here</em> we are demonstrating the freshdesk integration ticket creation command",
"DescriptionText": "Here we are demonstrating the freshdesk integration ticket creation command",
"Email": "jeffrey.collins@gmail.com",
"Name": "Jeff Testman"
},
"ID": 108,
"CreatedAt": "2019-02-05T15:55:35Z",
"Subject": "Demonstrate Ticket Creation"
}
}
Human Readable Output
Newly Created Ticket #108
| Status | DueBy | FrDueBy | Priority | Source | Tag | RequesterID | UpdatedAt | AdditionalFields | ID | CreatedAt | Subject |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2019-02-07T09:00:00Z | 2019-02-06T10:00:00Z | 3 | 2 | attention needed, billing related, new |
2043024010476 | 2019-02-05T15:55:35Z | DescriptionHTML: Herewe are demonstrating the freshdesk integration ticket creation command DescriptionText: Here we are demonstrating the freshdesk integration ticket creation command Name: Jeff Testman Email: jeffrey.collins@gmail.com |
108 | 2019-02-05T15:55:35Z | Demonstrate Ticket Creation |
2. Get ticket details
Gets details of a ticket, specified by the ticket ID number.
Base Command
fd-get-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID number of the ticket to fetch | Required |
| include_requester | If set to true, the ticket requester’s ID, email address, mobile number, name, and phone number will be included in the ticket’s output. Note that this is not set by default because setting this to ‘true’ will consume an additional API credit per API call.For more information, see the Freshdesk API documentation. |
Optional |
| include_stats | If set to true then the ticket’s closed_at, resolved_at and first_responded_at time will be included in the response. Note that this is not set by default because setting this to ‘true’ will consume an additional API credit per API call.For more information, see the Freshdesk API documentation. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.ID | Number | ID number of the fetched ticket |
| Freshdesk.Ticket.Priority | Number | Ticket priority |
| Freshdesk.Ticket.DueBy | Date | Timestamp that denotes when the ticket is due to be resolved |
| Freshdesk.Ticket.Subject | String | Ticket subject |
| Freshdesk.Ticket.Status | Number | Ticket status |
| Freshdesk.Ticket.RequesterID | Number | User ID of the requester |
| Freshdesk.Ticket.Tag | Unknown | Tags associated with the ticket |
| Freshdesk.Ticket.GroupID | Number | ID of the group the ticket was assigned to |
| Freshdesk.Ticket.Source | Number | The channel through which the ticket was created |
| Freshdesk.Ticket.CreatedAt | Date | Ticket creation timestamp |
| Freshdesk.Ticket.ResponderID | Number | ID of the agent the ticket was assigned to |
| Freshdesk.Ticket.FrDueBy | Date | Timestamp that denotes when the first response is due |
| Freshdesk.Ticket.Conversation | Unknown | Conversations associated with this ticket |
| Freshdesk.Ticket.Attachment.AttachmentURL | Unknown | URL to download the file attached to the ticket to your local machine |
| Freshdesk.Ticket.Attachment.Name | Unknown | The name of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ID | Unknown | ID number of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.Size | String | Size of the file attached to the ticket |
| Freshdesk.Ticket.UpdatedAt | Date | Ticket update timestamp |
Command Example
!fd-get-ticket id=108 include_requester=true include_stats=true
Context Example
{
"Freshdesk.Ticket": {
"Status": 2,
"DueBy": "2019-02-07T09:00:00Z",
"FrDueBy": "2019-02-06T10:00:00Z",
"Priority": 3,
"Source": 2,
"Tag": [
"attention needed",
"billing related",
"new"
],
"RequesterID": 2043024010476,
"UpdatedAt": "2019-02-05T15:55:35Z",
"AdditionalFields": {
"Stats": {
"StatusUpdatedAt": "2019-02-05T15:55:35Z"
},
"IsEscalated": false,
"Deleted": false,
"Spam": false,
"FrEscalated": false,
"Requestor": {
"Name": "Jeff Testman",
"Email": "jeffrey.collins@gmail.com",
"Id": 2043024010476
},
"DescriptionHTML": "<em>Here</em> we are demonstrating the freshdesk integration ticket creation command",
"DescriptionText": "Here we are demonstrating the freshdesk integration ticket creation command"
},
"ID": 108,
"CreatedAt": "2019-02-05T15:55:35Z",
"Subject": "Demonstrate Ticket Creation"
}
}
Human Readable Output
Viewing Ticket #108
| Status | DueBy | FrDueBy | Priority | Source | Tag | RequesterID | UpdatedAt | AdditionalFields | ID | CreatedAt | Subject |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2019-02-07T09:00:00Z | 2019-02-06T10:00:00Z | 3 | 2 | attention needed, billing related, new |
2043024010476 | 2019-02-05T15:55:35Z | DescriptionHTML: Here we are demonstrating the freshdesk integration ticket creation command IsEscalated: false DescriptionText: Here we are demonstrating the freshdesk integration ticket creation command Spam: false FrEscalated: false |
108 | 2019-02-05T15:55:35Z | Demonstrate Ticket Creation |
3. Update a ticket
Updates a ticket specified by the ID.
Base Command
fd-update-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| subject | Subject of the ticket | Optional |
| status | Status of the ticket. Each number has a corresponding value. 2 is Open, 3 is Pending, 4 is Resolved, 5 is Closed, 6 is Waiting on Customer, 7 is Waiting on Third Party. Acceptable values for this command argument are the digits 2,3,4,5,6,7, or their corresponding strings ‘Open’,‘Pending’,‘Resolved’,‘Closed’,‘Waiting on Customer’,‘Waiting on Third Party’. |
Optional |
| priority | Priority of the ticket. Each number has a corresponding value. 1 is Low, 2 is Medium, 3 is High, 4 is Urgent. Acceptable values for this command argument are the digits 1,2,3,4, or their corresponding strings ‘Low’,‘Medium’,‘High’,‘Urgent’. |
Optional |
| description | HTML content of the ticket. | Optional |
| id | ID number of the ticket to update | Required |
| assigned_agent | Update which agent is assigned to respond to this ticket by entering either their unique agent ID, name, or email. | Optional |
| assigned_group | Update the group assigned to respond to this ticket by entering the group’s unique ID or the name of the group. | Optional |
| additional_fields | Fields not included in the default command arguments that you wish to enter the value for. Format - “field=value” where field value pairs are delimited from subsequent pairs by a semicolon symbol ‘;’ and where values that are lists are delimited by commas ‘,’. For example: “name=Jeffrey Collins;email=jeffrey.collins@gmail.com;tags=new,attention needed,billing related” |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.ID | Number | ID of the updated ticket |
| Freshdesk.Ticket.Priority | Number | Ticket priority |
| Freshdesk.Ticket.DueBy | Date | Timestamp that denotes when the ticket is due to be resolved |
| Freshdesk.Ticket.Subject | String | Ticket subject |
| Freshdesk.Ticket.Status | Number | Ticket status |
| Freshdesk.Ticket.RequesterID | Number | User ID of the requester |
| Freshdesk.Ticket.Tag | Unknown | Tags associated with the ticket |
| Freshdesk.Ticket.GroupID | Number | ID of the group assigned to the ticket |
| Freshdesk.Ticket.Source | Number | The channel through which the ticket was created |
| Freshdesk.Ticket.CreatedAt | Date | Ticket creation timestamp |
| Freshdesk.Ticket.ResponderId | Number | ID of the agent the ticket was assigned to |
| Freshdesk.Ticket.FrDueBy | Date | Timestamp that denotes when the first response is due |
| Freshdesk.Ticket.UpdatedAt | Date | Ticket update timestamp |
| Freshdesk.Ticket.AdditionalFields | Unknown | Additional fields and values that were updated using the ‘additional_fields’ argument |
| Freshdesk.Ticket.Attachment.AttachmentURL | Unknown | URL to download the file attached to the ticket to your local machine |
| Freshdesk.Ticket.Attachment.Name | Unknown | The name of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ContentType | String | Content type of the attached file |
| Freshdesk.Ticket.Attachment.ID | Unknown | ID number for the attached file |
| Freshdesk.Ticket.Attachment.Size | String | Size of the attached file in bytes |
Command Example
!fd-update-ticket id=108 priority=Medium subject="Demonstrating Ticket Updating" assigned_agent=jeffrey.collins@gmail.com additional_fields="tags=almost completed,yep;attachments=2@5a0be47f-748f-4d60-8f46-81feb8f0c438"
Context Example
{
"Freshdesk.Ticket": {
"Status": 2,
"ResponderID": 2043022085976,
"DueBy": "2019-02-06T15:00:00Z",
"FrDueBy": "2019-02-06T14:00:00Z",
"Priority": 2,
"Source": 2,
"Tag": [
"almost completed",
"yep"
],
"Attachment": [
{
"Name": "sample_attachment.md",
"Size": 76,
"ContentType": "application/octet-stream",
"ID": 2043010708407,
"AttachmentURL": "https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708407/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155538Z&X-Amz-Expires=86400&X-Amz-Signature=05dfe20c475c01763766b71ee0d4f0e808cef5fa116cede418eabafce4802947&X-Amz-SignedHeaders=Host"
}
],
"RequesterID": 2043024010476,
"UpdatedAt": "2019-02-05T15:55:37Z",
"AdditionalFields": {
"DescriptionHTML": "<em>Here</em> we are demonstrating the freshdesk integration ticket creation command",
"DescriptionText": "Here we are demonstrating the freshdesk integration ticket creation command"
},
"ID": 108,
"CreatedAt": "2019-02-05T15:55:35Z",
"Subject": "Demonstrating Ticket Updating"
}
}
Human Readable Output
Ticket #108 Updated
| Status | ResponderID | DueBy | FrDueBy | Priority | Source | Tag | Attachment | RequesterID | UpdatedAt | AdditionalFields | ID | CreatedAt | Subject |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2043022085976 | 2019-02-06T15:00:00Z | 2019-02-06T14:00:00Z | 2 | 2 | almost completed, yep |
ID: 2043010708407, Size: 76, ContentType: application/octet-stream, Name: sample_attachment.md, AttachmentURL: https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708407/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155538Z&X-Amz-Expires=86400&X-Amz-Signature=05dfe20c475c01763766b71ee0d4f0e808cef5fa116cede418eabafce4802947&X-Amz-SignedHeaders=Host | 2043024010476 | 2019-02-05T15:55:37Z | DescriptionHTML: Here we are demonstrating the freshdesk integration ticket creation command DescriptionText: Here we are demonstrating the freshdesk integration ticket creation command |
108 | 2019-02-05T15:55:35Z | Demonstrating Ticket Updating |
4. Get a list of all contacts
Returns a list of all contacts matching the specified filters. If no filters are provided then all unblocked and undeleted contacts will be returned.
Base Command
fd-list-contacts
Input
| Argument Name | Description | Required |
|---|---|---|
| mobile | mobile number to filter the contacts by | Optional |
| phone | phone number to filter contacts by | Optional |
| state | The state of contacts by which you want to filter the contacts | Optional |
| updated_since | Return contacts that have been updated after the timestamp given as this argument value. Acceptable format is ‘YYYY-MM-DDTHH:MM:SSZ’ For example: ‘2018-01-19T02:00:00Z’. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Contact.Active | Boolean | Set to true if the contact has been verified |
| Freshdesk.Contact.Address | String | Address of the contact |
| Freshdesk.Contact.CompanyID | Number | ID of the primary company to which this contact belongs |
| Freshdesk.Contact.ViewAllTickets | Boolean | Set to true if the contact can see all tickets that are associated with the company to which s/he belong |
| Freshdesk.Contact.Deleted | Boolean | Set to true if the contact has been deleted |
| Freshdesk.Contact.Description | String | A short description of the contact |
| Freshdesk.Contact.Email | String | Primary email address of the contact |
| Freshdesk.Contact.ID | Number | ID of the contact |
| Freshdesk.Contact.JobTitle | String | Job Title of the contact |
| Freshdesk.Contact.Language | String | Language of the contact |
| Freshdesk.Contact.Mobile | Number | Mobile number of the contact |
| Freshdesk.Contact.Name | String | Name of the contact |
| Freshdesk.Contact.Phone | Number | Telephone number of the contact |
| Freshdesk.Contact.Tag | Unknown | Tags associated with this contact |
| Freshdesk.Contact.TimeZone | String | Time zone in which the contact resides |
| Freshdesk.Contact.TwitterID | String | Twitter handle of the contact |
| Freshdesk.Contact.UniqueExternalID | String | External ID of the contact |
| Freshdesk.Contact.CreatedAt | Date | Contact creation stamp |
| Freshdesk.Contact.UpdatedAt | Date | Contact updated timestamp |
Command Example
!fd-list-contacts updated_since=2018-01-19T02:00:00Z
Context Example
{
"Freshdesk.Contact": [
{
"Name": "Bob Tree",
"Language": "en",
"CompanyID": 44000302032,
"Mobile": "+972501231231",
"JobTitle": "Security Researcher",
"UpdatedAt": "2019-01-20T09:51:11Z",
"UniqueExternalID": "12345",
"Email": "bob.tree@freshdesk.com",
"Phone": "+972501231231",
"Address": "Rothchild 45",
"TimeZone": "Athens",
"ID": 2043022085984,
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Clarice Boone",
"Language": "en",
"ID": 2043022085990,
"UpdatedAt": "2019-01-20T09:06:34Z",
"TimeZone": "Athens",
"Email": "clboone@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:34Z"
},
{
"Name": "Emily Testman",
"Language": "en",
"ID": 2043022085989,
"UpdatedAt": "2019-01-20T09:06:34Z",
"TimeZone": "Athens",
"Email": "emily.dean@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:34Z"
},
{
"Name": "Finch Hoot",
"Language": "en",
"ID": 2043022085991,
"UpdatedAt": "2019-01-20T09:06:34Z",
"TimeZone": "Athens",
"Email": "finchhoot1@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:34Z"
},
{
"Name": "James Dean",
"Language": "en",
"ID": 2043022085977,
"UpdatedAt": "2019-01-20T09:06:32Z",
"TimeZone": "Athens",
"Email": "james@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:32Z"
},
{
"Name": "Jeff Testman",
"Language": "en",
"ID": 2043024010476,
"UpdatedAt": "2019-02-05T10:30:16Z",
"TimeZone": "Athens",
"Email": "jeffrey.collins@gmail.com",
"CreatedAt": "2019-02-05T10:30:16Z"
},
{
"Name": "Joe Mathew",
"Language": "en",
"ID": 2043022085982,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "joe.mathew@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Johnny Appleseed",
"Language": "en",
"ID": 2043022085986,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "johnny.appleseed@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Lewis Testman",
"Language": "en",
"ID": 2043022085987,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "lewis.clarke@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Maria Von Trapp",
"Language": "en",
"ID": 2043022085988,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "soundofmusic@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Mark Colbert",
"Language": "en",
"ID": 2043022085992,
"UpdatedAt": "2019-01-20T09:06:34Z",
"TimeZone": "Athens",
"Email": "mark.colbert@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:34Z"
},
{
"Name": "Matt Rogers",
"Language": "en",
"ID": 2043022085980,
"UpdatedAt": "2019-01-20T09:06:32Z",
"TimeZone": "Athens",
"Email": "matt.rogers@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:32Z"
},
{
"Name": "Pedro Testez",
"ID": 2043023084321,
"UpdatedAt": "2019-01-28T14:16:49Z",
"TimeZone": "Athens",
"Email": "pedmart@gmail.com",
"CreatedAt": "2019-01-28T14:16:49Z"
},
{
"Name": "Phileas Fogg",
"Language": "en",
"ID": 2043022085985,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "aroundtheworld80@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Rachel Doe",
"Language": "en",
"ID": 2043022085978,
"Phone": "1 866 832 3090",
"UpdatedAt": "2019-01-20T09:06:32Z",
"TimeZone": "Athens",
"Email": "rachel@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:32Z"
},
{
"Name": "Sam Kart",
"Language": "en",
"ID": 2043022085983,
"UpdatedAt": "2019-01-20T09:06:33Z",
"TimeZone": "Athens",
"Email": "sam.kart@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:33Z"
},
{
"Name": "Sam Osborne",
"Language": "en",
"ID": 2043022085995,
"UpdatedAt": "2019-01-20T09:06:47Z",
"TimeZone": "Athens",
"Email": "sam.ozzy@freshdesk.com",
"CreatedAt": "2019-01-20T09:06:47Z"
}
]
}
Human Readable Output
Contacts Filtered by _updated_since: 2018-01-19T02:00:00Z
| Name | Language | TwitterID | UpdatedAt | TimeZone | ID | CreatedAt |
|---|---|---|---|---|---|---|
| Bob Tree | en | 2019-01-20T09:51:11Z | Athens | 2043022085984 | 2019-01-20T09:06:33Z | |
| Clarice Boone | en | 2019-01-20T09:06:34Z | Athens | 2043022085990 | 2019-01-20T09:06:34Z | |
| Emily Testman | en | 2019-01-20T09:06:34Z | Athens | 2043022085989 | 2019-01-20T09:06:34Z | |
| Finch Hoot | en | 2019-01-20T09:06:34Z | Athens | 2043022085991 | 2019-01-20T09:06:34Z | |
| James Dean | en | 2019-01-20T09:06:32Z | Athens | 2043022085977 | 2019-01-20T09:06:32Z | |
| Jeff Testman | en | 2019-02-05T10:30:16Z | Athens | 2043024010476 | 2019-02-05T10:30:16Z | |
| Joe Mathew | en | 2019-01-20T09:06:33Z | Athens | 2043022085982 | 2019-01-20T09:06:33Z | |
| Johnny Appleseed | en | 2019-01-20T09:06:33Z | Athens | 2043022085986 | 2019-01-20T09:06:33Z | |
| Lewis Testman | en | 2019-01-20T09:06:33Z | Athens | 2043022085987 | 2019-01-20T09:06:33Z | |
| Maria Von Trapp | en | 2019-01-20T09:06:33Z | Athens | 2043022085988 | 2019-01-20T09:06:33Z | |
| Mark Colbert | en | 2019-01-20T09:06:34Z | Athens | 2043022085992 | 2019-01-20T09:06:34Z | |
| Matt Rogers | en | 2019-01-20T09:06:32Z | Athens | 2043022085980 | 2019-01-20T09:06:32Z | |
| Pedro Testez | 2019-01-28T14:16:49Z | Athens | 2043023084321 | 2019-01-28T14:16:49Z | ||
| Phileas Fogg | en | 2019-01-20T09:06:33Z | Athens | 2043022085985 | 2019-01-20T09:06:33Z | |
| Rachel Doe | en | 2019-01-20T09:06:32Z | Athens | 2043022085978 | 2019-01-20T09:06:32Z | |
| Sam Kart | en | 2019-01-20T09:06:33Z | Athens | 2043022085983 | 2019-01-20T09:06:33Z | |
| Sam Osborne | en | 2019-01-20T09:06:47Z | Athens | 2043022085995 | 2019-01-20T09:06:47Z |
5. Get contact details
View a contact’s details specified by the ID number.
Base Command
fd-get-contact
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the contact you wish to view the details of. To find ID numbers for your contacts try executing the fd-list-contacts command. |
Optional |
| mobile | Mobile number of the contact you wish to view the details of | Optional |
| Email address of the contact you wish to view the details of | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Contact.Active | Boolean | Set to true if the contact has been verified |
| Freshdesk.Contact.Address | String | Address of the contact |
| Freshdesk.Contact.CompanyID | Number | ID of the primary company to which this contact belongs |
| Freshdesk.Contact.ViewAllTickets | Boolean | Set to true if the contact can see all tickets that are associated with the company to which s/he belong |
| Freshdesk.Contact.Deleted | Boolean | Set to true if the contact has been deleted |
| Freshdesk.Contact.Description | String | A short description of the contact |
| Freshdesk.Contact.Email | String | Primary email address of the contact |
| Freshdesk.Contact.Id | Number | ID of the contact |
| Freshdesk.Contact.JobTitle | String | Job Title of the contact |
| Freshdesk.Contact.Language | String | Language of the contact |
| Freshdesk.Contact.Mobile | Number | Mobile number of the contact |
| Freshdesk.Contact.Name | String | Name of the contact |
| Freshdesk.Contact.Phone | Number | Telephone number of the contact |
| Freshdesk.Contact.Tag | Unknown | Tags associated with this contact |
| Freshdesk.Contact.TimeZone | String | Time zone in which the contact resides |
| Freshdesk.Contact.TwitterID | String | Twitter handle of the contact |
| Freshdesk.Contact.UniqueExternalID | String | External ID of the contact |
| Freshdesk.Contact.CreatedAt | Date | Contact creation timestamp |
| Freshdesk.Contact.UpdatedAt | Date | Contact updated timestamp |
Command Example
!fd-get-contact id=2043022085984
Context Example
{
"Freshdesk.Contact": {
"Name": "Bob Tree",
"Language": "en",
"CompanyID": 44000302032,
"Mobile": "+972501231231",
"JobTitle": "Security Researcher",
"UpdatedAt": "2019-01-20T09:51:11Z",
"UniqueExternalID": "12345",
"Email": "bob.tree@freshdesk.com",
"Phone": "+972501231231",
"Tag": [
"security"
],
"Address": "Rothchild 45",
"TimeZone": "Athens",
"ID": 2043022085984,
"CreatedAt": "2019-01-20T09:06:33Z"
}
}
Human Readable Output
Viewing Contact #2043022085984
| Address | Language | CompanyID | Mobile | JobTitle | UniqueExternalID | ID | Phone | Tag | UpdatedAt | TimeZone | CreatedAt | Name | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Rothchild 45 | en | 44000302032 | +972501231231 | Security Researcher | 12345 | 2043022085984 | +972501231231 | security | 2019-01-20T09:51:11Z | Athens | bob.tree@freshdesk.com | 2019-01-20T09:06:33Z | Bob Tree |
6. Get a list of all canned response folders
Returns a list of all canned response folders (only users with Admin privileges).
Base Command
fd-list-canned-response-folders
Input
There is no input for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.CRFolder.ID | Number | Unique ID of the canned response folder |
| Freshdesk.CRFolder.Name | String | Name of the canned response folder |
| Freshdesk.CRFolder.Personal | Boolean | Set true if the folder can only be accessed by you |
| Freshdesk.CRFolder.ResponsesCount | Number | Number of canned responses in the folder |
| Freshdesk.CRFolder.CreatedAt | Date | Canned Response Folder’s creation timestamp |
| Freshdesk.CRFolder.UpdatedAt | Date | Canned Response Folder’s updated timestamp |
Command Example
!fd-list-canned-response-folders
Context Example
{
"Freshdesk.CRFolder": [
{
"Personal": true,
"ResponsesCount": 1,
"ID": 2043000174274,
"Name": "Personal"
}
]
}
Human Readable Output
All Canned Response Folders
| Personal | Name | ID | ResponsesCount |
|---|---|---|---|
| true | Personal | 2043000174274 | 1 |
7. Get a list of details for all canned responses in a folder
Returns a list of details for all canned responses in a folder.
Base Command
fd-get-canned-response-folder
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the Folder containing the Canned Responses you wish to view the details of. To find ID numbers for your Canned Response folders try executing the fd-list-canned-response-folders command. |
Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.CRFolder.CR.ID | Number | Unique ID of the canned response |
| Freshdesk.CRFolder.CR.Title | String | Title of the canned response |
| Freshdesk.CRFolder.CR.FolderID | Number | ID of the containing folder |
| Freshdesk.CRFolder.CR.Content | String | Plaintext version of the canned response content |
| Freshdesk.CRFolder.CR.ContentHTML | String | HTML version of the canned response content |
| Freshdesk.CRFolder.CR.Attachment.AttachmentURL | String | URL to download the file attached to the ticket to your local machine |
| Freshdesk.CRFolder.CR.Attachment.Name | String | The name of the file attached to the ticket |
| Freshdesk.CRFolder.CR.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.CRFolder.CR.Attachment.ID | Number | ID number for the file attached to the ticket |
| Freshdesk.CRFolder.CR.Attachment.Size | Number | Size of the file attached to the file |
Command Example
!fd-get-canned-response-folder id=2043000174274
Context Example
{
"Freshdesk.CRFolder": [
{
"Content": "Thank you for reaching out to us. Our team will look into your request and get back to you shortly. \n \n \n You can check the status of your request and add comments here:\n \n {{ticket.url}}\n \n \n Regards, \n {{ticket.agent.name}}",
"FolderID": 2043000174274,
"ContentHTML": "<div dir=\"ltr\">\n Thank you for reaching out to us. Our team will look into your request and get back to you shortly. \n <br>\n <br>\n You can check the status of your request and add comments here:\n <br>\n {{ticket.url}}\n <br>\n <br>\n Regards,<br>\n {{ticket.agent.name}}\n </div>\n ",
"ID": 2043000056698,
"Title": "We\u2019ve received your request"
}
]
}
Human Readable Output
Details of Canned Responses in CR Folder #2043000174274
| Content | FolderID | ContentHTML | ID | Title |
|---|---|---|---|---|
| Thank you for reaching out to us. Our team will look into your request and get back to you shortly. You can check the status of your request and add comments here: {{ticket.url}} Regards, {{ticket.agent.name}} |
2043000174274 |
Thank you for reaching out to us. Our team will look into your request and get back to you shortly. You can check the status of your request and add comments here: {{ticket.url}} Regards, {{ticket.agent.name}} |
2043000056698 | We’ve received your request |
8. Get a list of all groups
Returns a list of all groups.
Base Command
fd-list-groups
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Group.AgentID | Unknown | Array of agent user IDs separated by commas |
| Freshdesk.Group.AutoTicketAssign | Boolean | Set to true when automatic ticket assignment was enabled. Automatic ticket assignment is only available on certain plans |
| Freshdesk.Group.BusinessHourID | Number | Unique ID of the business hour associated with the group |
| Freshdesk.Group.Description | String | Description of the group |
| Freshdesk.Group.EscalateTo | Number | The ID of the user that an escalation email is sent to if a ticket is unassigned |
| Freshdesk.Group.ID | Number | Unique ID of the group |
| Freshdesk.Group.Name | String | Name of the group |
| Freshdesk.Group.UnassignedFor | String | The time after which an escalation email is sent if a ticket remains unassigned |
| Freshdesk.Group.CreatedAt | Date | Group creation timestamp |
| Freshdesk.Group.UpdatedAt | Date | Grup updated timestamp |
| Freshdesk.Group.GroupType | String | Group Type of the group |
Command Example
!fd-list-groups
Context Example
{
"Freshdesk.Group": [
{
"GroupType": "support_agent_group",
"Name": "Account managers",
"UpdatedAt": "2019-01-20T09:06:49Z",
"ID": 2043000867330,
"CreatedAt": "2019-01-20T09:06:49Z",
"Description": "Account managers"
},
{
"GroupType": "support_agent_group",
"Name": "Billing",
"UpdatedAt": "2019-01-20T09:06:31Z",
"ID": 2043000867325,
"CreatedAt": "2019-01-20T09:06:31Z",
"Description": "Members of the Billing team belong to this group"
},
{
"GroupType": "support_agent_group",
"Name": "Customer Support",
"UpdatedAt": "2019-01-20T09:06:47Z",
"ID": 2043000867327,
"CreatedAt": "2019-01-20T09:06:47Z",
"Description": "Customer Support"
},
{
"GroupType": "support_agent_group",
"Name": "Development",
"UpdatedAt": "2019-01-20T09:06:49Z",
"ID": 2043000867329,
"CreatedAt": "2019-01-20T09:06:49Z",
"Description": "Development"
},
{
"GroupType": "support_agent_group",
"Name": "Escalations",
"UpdatedAt": "2019-01-20T09:06:31Z",
"ID": 2043000867326,
"CreatedAt": "2019-01-20T09:06:31Z",
"Description": "Team to handle Customer escalations"
},
{
"GroupType": "support_agent_group",
"Name": "Login and security",
"UpdatedAt": "2019-01-20T09:06:47Z",
"ID": 2043000867328,
"CreatedAt": "2019-01-20T09:06:47Z",
"Description": "Login and security"
},
{
"GroupType": "support_agent_group",
"Name": "Product Management",
"UpdatedAt": "2019-01-20T09:06:31Z",
"ID": 2043000867322,
"CreatedAt": "2019-01-20T09:06:31Z",
"Description": "Product Management group"
},
{
"GroupType": "support_agent_group",
"Name": "QA",
"UpdatedAt": "2019-01-20T09:06:31Z",
"ID": 2043000867323,
"CreatedAt": "2019-01-20T09:06:31Z",
"Description": "Members of the QA team belong to this group"
},
{
"GroupType": "support_agent_group",
"Name": "Sales",
"UpdatedAt": "2019-01-20T09:06:31Z",
"ID": 2043000867324,
"CreatedAt": "2019-01-20T09:06:31Z",
"Description": "People in the Sales team are members of this group"
}
]
}
Human Readable Output
All Groups
| GroupType | Description | UpdatedAt | ID | CreatedAt | Name |
|---|---|---|---|---|---|
| support_agent_group | Account managers | 2019-01-20T09:06:49Z | 2043000867330 | 2019-01-20T09:06:49Z | Account managers |
| support_agent_group | Members of the Billing team belong to this group | 2019-01-20T09:06:31Z | 2043000867325 | 2019-01-20T09:06:31Z | Billing |
| support_agent_group | Customer Support | 2019-01-20T09:06:47Z | 2043000867327 | 2019-01-20T09:06:47Z | Customer Support |
| support_agent_group | Development | 2019-01-20T09:06:49Z | 2043000867329 | 2019-01-20T09:06:49Z | Development |
| support_agent_group | Team to handle Customer escalations | 2019-01-20T09:06:31Z | 2043000867326 | 2019-01-20T09:06:31Z | Escalations |
| support_agent_group | Login and security | 2019-01-20T09:06:47Z | 2043000867328 | 2019-01-20T09:06:47Z | Login and security |
| support_agent_group | Product Management group | 2019-01-20T09:06:31Z | 2043000867322 | 2019-01-20T09:06:31Z | Product Management |
| support_agent_group | Members of the QA team belong to this group | 2019-01-20T09:06:31Z | 2043000867323 | 2019-01-20T09:06:31Z | QA |
| support_agent_group | People in the Sales team are members of this group | 2019-01-20T09:06:31Z | 2043000867324 | 2019-01-20T09:06:31Z | Sales |
9. Add a reply to a ticket
Adds a reply to a specified ticket.
Base Command
fd-ticket-reply
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | ID of the ticket to add a reply to | Required |
| body | Content of the reply (in HTML format) | Required |
| from_email | The email address from which the reply is sent. By default, the global support email is used. | Optional |
| user_id | ID of the agent who is adding the reply to the ticket | Optional |
| cc_emails | CSV list of email addresses to add to the ‘cc’ field of the outgoing ticket email, e.g., "example1@gmail.com,example2@gmail.com" | Optional |
| bcc_emails | CSV list of email addresses to add to the ‘bcc’ field of the outgoing ticket email, e.g., "example1@gmail.com,example2@gmail.com" | Optional |
| attachments | CSV list of Entry IDs of files to attach to the reply, e.g., “468@73f988d1-bda2-4adc-8e02-926f02190070,560@73f988d1-bda2-4adc-8e02-926f02190070”. The total size of these attachments cannot exceed 15MB. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.Conversation.BodyHTML | String | Content of the conversation (in HTML format) |
| Freshdesk.Ticket.Conversation.BodyText | String | Content of the conversation (in plain text format) |
| Freshdesk.Ticket.Conversation.ID | Number | ID of the conversation |
| Freshdesk.Ticket.Conversation.Incoming | Boolean | Set to true when a particular conversation should appear as being created outside of the web portal |
| Freshdesk.Ticket.Conversation.ToEmail | Unknown | Array of email addresses of agents/users who need to be notified about this conversation |
| Freshdesk.Ticket.Conversation.Private | Boolean | Set to true if the note is private |
| Freshdesk.Ticket.Conversation.Source | Number | Denotes the type of conversation |
| Freshdesk.Ticket.Conversation.SupportEmail | String | Email address from which the reply is sent. For notes |
| Freshdesk.Ticket.Conversation.TicketID | Number | ID of the ticket that the conversation was added to |
| Freshdesk.Ticket.Conversation.UserID | Number | ID of the agent/user who added the conversation |
| Freshdesk.Ticket.Conversation.CreatedAt | Date | Conversation creation timestamp |
| Freshdesk.Ticket.Conversation.UpdatedAt | Date | Conversation updated timestamp |
| Freshdesk.Ticket.Conversation.FromEmail | String | The email address that the reply was sent from. By default |
| Freshdesk.Ticket.Conversation.Attachment.AttachmentURL | String | URL of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Name | String | The name of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ID | Number | ID number of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Size | Number | Size of the file attached to the ticket |
Command Example
!fd-ticket-reply ticket_id=108 body="Demonstrating the Ticket Reply Command" attachments=2@5a0be47f-748f-4d60-8f46-81feb8f0c438 cc_emails=example1@gmail.com,example2@gmail.com,example3@gmail.com
Context Example
{
"Freshdesk.Ticket": {
"Conversation": {
"BodyText": "Demonstrating the Ticket Reply Command",
"UserID": 2043022085976,
"Attachment": [
{
"Name": "sample_attachment.md",
"Size": 76,
"ContentType": "application/octet-stream",
"ID": 2043010708409,
"AttachmentURL": "https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708409/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155542Z&X-Amz-Expires=86400&X-Amz-Signature=9b19a5795e98af94f5b878a62db473b943986d59496e91f046ab4368b0b4f461&X-Amz-SignedHeaders=Host"
}
],
"UpdatedAt": "2019-02-05T15:55:42Z",
"AdditionalFields": {
"CCEmail": [
"example1@gmail.com",
"example2@gmail.com",
"example3@gmail.com"
],
"BodyHTML": "<div>Demonstrating the Ticket Reply Command</div>",
"ToEmail": [
"example@gmail.com"
],
"TicketID": 108,
"FromEmail": "Demisto <support@demistohelp.freshdesk.com>"
},
"ID": 44007154227,
"CreatedAt": "2019-02-05T15:55:42Z"
},
"ID": 108
}
}
Human Readable Output
Reply to Ticket #108
| BodyText | UserID | Attachment | UpdatedAt | AdditionalFields | ID | CreatedAt |
|---|---|---|---|---|---|---|
| Demonstrating the Ticket Reply Command | 2043022085976 | ID: 2043010708409, Size: 76, ContentType: application/octet-stream, Name: sample_attachment.md, AttachmentURL: https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708409/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155542Z&X-Amz-Expires=86400&X-Amz-Signature=9b19a5795e98af94f5b878a62db473b943986d59496e91f046ab4368b0b4f461&X-Amz-SignedHeaders=Host | 2019-02-05T15:55:42Z | CCEmail: example1@gmail.com, example2@gmail.com, example3@gmail.com TicketID: 108 BodyHTML: Demonstrating the Ticket Reply Command
ToEmail: example@gmail.com FromEmail: Demisto support@demistohelp.freshdesk.com |
44007154227 | 2019-02-05T15:55:42Z |
10. Get a list of all replies and notes for a ticket
Returns a list of all replies and notes for a specified ticket.
Base Command
fd-get-ticket-conversations
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | ID of the ticket that you want to list all conversations for | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.Conversation.BodyHTML | String | Content of the conversation (in HTML format) |
| Freshdesk.Ticket.Conversation.BodyText | String | Content of the conversation (in plain text format) |
| Freshdesk.Ticket.Conversation.ID | Number | ID of the conversation |
| Freshdesk.Ticket.Conversation.Incoming | Boolean | Set to true when a particular conversation should appear as being created outside of the web portal |
| Freshdesk.Ticket.Conversation.ToEmail | Unknown | Array of email addresses of agents/users who need to be notified about this conversation |
| Freshdesk.Ticket.Conversation.Private | Boolean | Set to true if the note is private |
| Freshdesk.Ticket.Conversation.Source | Number | Denotes the type of conversation |
| Freshdesk.Ticket.Conversation.SupportEmail | String | Email address from which the reply is sent. For notes |
| Freshdesk.Ticket.Conversation.TicketID | Number | ID of the ticket that the conversation was added to |
| Freshdesk.Ticket.Conversation.UserID | Number | ID of the agent/user who added the conversation |
| Freshdesk.Ticket.Conversation.CreatedAt | Date | Conversation creation timestamp |
| Freshdesk.Ticket.Conversation.UpdatedAt | Date | Conversation updated timestamp |
| Freshdesk.Ticket.Conversation.FromEmail | String | The email address that the reply was sent from. By default |
| Freshdesk.Ticket.Conversation.Attachment.AttachmentURL | String | URL of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Name | String | The name of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ID | Number | ID number of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Size | Number | Size of the file attached to the ticket |
Command Example
!fd-get-ticket-conversations ticket_id=108
Context Example
{
"Freshdesk.Ticket": {
"Conversation": [
{
"Category": 3,
"BodyText": "Demonstrating the Ticket Reply Command",
"UserID": 2043022085976,
"Attachment": [
{
"Name": "sample_attachment.md",
"Size": 76,
"ContentType": "application/octet-stream",
"ID": 2043010708409,
"AttachmentURL": "https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708409/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155543Z&X-Amz-Expires=86400&X-Amz-Signature=f26a2ab003aa5a9a0862389ae1c4a44470bf06ba0acd0e60c742ca164c16dff2&X-Amz-SignedHeaders=Host"
}
],
"UpdatedAt": "2019-02-05T15:55:42Z",
"AdditionalFields": {
"TicketID": 108,
"CCEmail": [
"example1@gmail.com",
"example2@gmail.com",
"example3@gmail.com"
],
"BodyHTML": "<div>Demonstrating the Ticket Reply Command</div>",
"ToEmail": [
"example@gmail.com"
],
"FromEmail": "Demisto <support@demistohelp.freshdesk.com>",
"SupportEmail": "support@demistohelp.freshdesk.com"
},
"ID": 44007154227,
"CreatedAt": "2019-02-05T15:55:42Z"
}
],
"ID": 108
}
}
Human Readable Output
Conversations of Ticket #108
| Category | BodyText | UserID | Attachment | UpdatedAt | AdditionalFields | ID | CreatedAt |
|---|---|---|---|---|---|---|---|
| 3 | Demonstrating the Ticket Reply Command | 2043022085976 | ID: 2043010708409, Size: 76, ContentType: application/octet-stream, Name: sample_attachment.md, AttachmentURL: https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708409/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155543Z&X-Amz-Expires=86400&X-Amz-Signature=f26a2ab003aa5a9a0862389ae1c4a44470bf06ba0acd0e60c742ca164c16dff2&X-Amz-SignedHeaders=Host | 2019-02-05T15:55:42Z | FromEmail: Demisto support@demistohelp.freshdesk.com BodyHTML: Demonstrating the Ticket Reply Command
SupportEmail: support@demistohelp.freshdesk.com CCEmail: example1@gmail.com, example2@gmail.com, example3@gmail.com TicketID: 108 ToEmail: example@gmail.com |
44007154227 | 2019-02-05T15:55:42Z |
11. Get a list of all agents
Returns a list of agents that match the filter criteria.
Base Command
fd-list-agents
Input
| Argument Name | Description | Required |
|---|---|---|
| mobile | Mobile phone number to filter agents by. Enter the number without dashes or spaces between the numbers. Numbers should be entered as they appear in your Freshdesk web portal. If the number appears in your web portal with a plus sign and country code, then that is how you should enter here, e.g., ‘+972501231231’. |
Optional |
| phone | Telephone number to filter agents by. Enter the number without dashes or spaces between the numbers. Numbers should be entered as they appear in your Freshdesk web portal. If the number appears in your web portal with a plus sign and country code, then that is how you should enter it here, e.g., ‘+972501231231’. |
Optional |
| state | List all agents who are either ‘fulltime’ or ‘occasional’ | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Agent.Available | Boolean | Set to true when the agent is in a group that has enabled “Automatic Ticket Assignment” and is accepting new tickets |
| Freshdesk.Agent.AvailableSince | Date | Timestamp that denotes when the agent became available/unavailable (depending on the value of the ‘available’ attribute) |
| Freshdesk.Agent.ID | Number | User ID of the agent |
| Freshdesk.Agent.Occasional | Boolean | Set to true when the agent is an occasional agent (true => occasional |
| Freshdesk.Agent.Signature | String | Signature of the agent (in HTML format) |
| Freshdesk.Agent.TicketScope | Number | Ticket permission of the agent |
| (1 - Global Access, 2 - Group Access, 3 - Restricted Access) | ||
| Freshdesk.Agent.GroupID | Unknown | Group IDs associated with the agent |
| Freshdesk.Agent.RoleID | Unknown | Role IDs associated with the agent |
| Freshdesk.Agent.CreatedAt | Date | Agent creation timestamp |
| Freshdesk.Agent.UpdatedAt | Date | Agent updated timestamp |
| Freshdesk.Agent.Contact.Active | Boolean | Set to true when the agent is verified |
| Freshdesk.Agent.Contact.Email | String | Email Address of the agent |
| Freshdesk.Agent.Contact.JobTitle | String | Job title of the agent |
| Freshdesk.Agent.Contact.Language | String | Language of the agent. Default language is “en” |
| Freshdesk.Agent.Contact.LastLoginAt | Date | Timestamp of the agent’s last successful login |
| Freshdesk.Agent.Contact.Mobile | Number | Mobile number of the agent |
| Freshdesk.Agent.Contact.Name | String | Name of the agent |
| Freshdesk.Agent.Contact.Phone | Number | Telephone number of the agent |
| Freshdesk.Agent.Contact.TimeZone | String | Time zone of the agent |
| Freshdesk.Agent.Contact.CreatedAt | Date | Contact creation timestamp |
| Freshdesk.Agent.Contact.UpdatedAt | Date | Contact updated timestamp |
| Freshdesk.Agent.Type | String | Type of agent |
Command Example
!fd-list-agents state=fulltime
Context Example
{
"Freshdesk.Agent": [
{
"TicketScope": 1,
"Contact": {
"Name": "Jeff Testman",
"Language": "en",
"LastLoginAt": "2019-01-23T09:05:34Z",
"Phone": "506912312",
"UpdatedAt": "2019-01-20T09:14:26Z",
"Active": true,
"TimeZone": "Athens",
"Email": "jeffrey.collins@gmail.com",
"CreatedAt": "2019-01-20T09:06:31Z"
},
"UpdatedAt": "2019-02-04T17:12:33Z",
"Type": "support_agent",
"ID": 2043022085976,
"CreatedAt": "2019-01-20T09:06:31Z"
}
]
}
Human Readable Output
All Agents
| TicketScope | Contact | UpdatedAt | Type | ID | CreatedAt |
|---|---|---|---|---|---|
| 1 | Name: Jeff Testman Language: en LastLoginAt: 2019-01-23T09:05:34Z Phone: 506912312 UpdatedAt: 2019-01-20T09:14:26Z Active: true TimeZone: Athens Email: jeffrey.collins@gmail.com CreatedAt: 2019-01-20T09:06:31Z |
2019-02-04T17:12:33Z | support_agent | 2043022085976 | 2019-01-20T09:06:31Z |
12. Create a note for a ticket
Creates a note for a specified ticket. By default, notes are private. To make a note public, set the ‘private’ argument to false.
Base Command
fd-create-ticket-note
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | ID of the ticket to make a note for | Required |
| body | Content of the note (in HTML format) | Required |
| private | Set to false if the note is public | Optional |
| user_id | ID of the agent who is adding the note. To find agent ID numbers, run the ‘fd-list-agents’ command. |
Optional |
| notify_emails | CSV list of agent email addresses to be notify about this note, e.g., "example1@gmail.com,example2@gmail.com,example3@gmail.com" | Optional |
| incoming | Set to true if a particular note should appear as being created outside of the web portal | Optional |
| attachments | CSV list of entry IDs of files to attach to the note, e.g., “468@73f988d1-bda2-4adc-8e02-926f02190070,560@73f988d1-bda2-4adc-8e02-926f02190070”. The total size of these attachments cannot exceed 15MB. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.Conversation.BodyHTML | String | Content of the conversation (in HTML) |
| Freshdesk.Ticket.Conversation.BodyText | String | Content of the conversation (in plain text) |
| Freshdesk.Ticket.Conversation.ID | Number | ID of the conversation |
| Freshdesk.Ticket.Conversation.Incoming | Boolean | Set to true when a particular conversation should appear as being created outside of the web portal |
| Freshdesk.Ticket.Conversation.ToEmail | Unknown | List of agent/user email addresses of agents/users who need to be notified about this conversation |
| Freshdesk.Ticket.Conversation.Private | Boolean | Is the conversation private |
| Freshdesk.Ticket.Conversation.Source | Number | Conversation type |
| Freshdesk.Ticket.Conversation.SupportEmail | String | Email address the reply is sent from. For notes |
| Freshdesk.Ticket.Conversation.TicketID | Number | ID of the ticket the conversation was added to |
| Freshdesk.Ticket.Conversation.UserID | Number | ID of the agent/user who added the conversation |
| Freshdesk.Ticket.Conversation.CreatedAt | Date | Conversation creation timestamp |
| Freshdesk.Ticket.Conversation.UpdatedAt | Date | Conversation updated timestamp |
| Freshdesk.Ticket.Conversation.FromEmail | String | The email address that the reply/note was sent from. By default |
| Freshdesk.Ticket.Conversation.Attachment.AttachmentURL | String | URL of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Name | String | The name of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.ID | Number | ID number of the file attached to the ticket |
| Freshdesk.Ticket.Conversation.Attachment.Size | Number | ize of the file attached to the ticket |
Command Example
!fd-create-ticket-note ticket_id=108 body="Demonstrating the Ticket Reply Command" attachments=2@5a0be47f-748f-4d60-8f46-81feb8f0c438 incoming=true private=false
Context Example
{
"Freshdesk.Ticket": {
"Conversation": {
"BodyText": "Demonstrating the Ticket Reply Command",
"Incoming": true,
"UserID": 2043022085976,
"Attachment": [
{
"Name": "sample_attachment.md",
"Size": 76,
"ContentType": "application/octet-stream",
"ID": 2043010708410,
"AttachmentURL": "https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708410/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155545Z&X-Amz-Expires=86400&X-Amz-Signature=db5ba2faa0753ffbe06b5b575500eb3bf55eb6460feab531cc4a723ce9e15512&X-Amz-SignedHeaders=Host"
}
],
"UpdatedAt": "2019-02-05T15:55:45Z",
"AdditionalFields": {
"BodyHTML": "<div>Demonstrating the Ticket Reply Command</div>",
"TicketID": 108
},
"ID": 44007154233,
"CreatedAt": "2019-02-05T15:55:45Z"
},
"ID": 108
}
}
Human Readable Output
Note for Ticket #108
| BodyText | Incoming | UserID | Attachment | UpdatedAt | AdditionalFields | ID | CreatedAt |
|---|---|---|---|---|---|---|---|
| Demonstrating the Ticket Reply Command | true | 2043022085976 | ID: 2043010708410, Size: 76, ContentType: application/octet-stream, Name: sample_attachment.md, AttachmentURL: https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/2043010708410/original/sample_attachment.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJ2JSYZ7O3I4JO6DA%2F20190205%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190205T155545Z&X-Amz-Expires=86400&X-Amz-Signature=db5ba2faa0753ffbe06b5b575500eb3bf55eb6460feab531cc4a723ce9e15512&X-Amz-SignedHeaders=Host | 2019-02-05T15:55:45Z | TicketID: 108 BodyHTML: Demonstrating the Ticket Reply Command
|
44007154233 | 2019-02-05T15:55:45Z |
13. Delete a ticket
Deletes a ticket, specified by ID.
Base Command
fd-delete-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the ticket to delete | Required |
Context Output
There is no context output for this command.
Command Example
!fd-delete-ticket id=108
Context Example
{
"Freshdesk.Ticket": {
"AdditionalFields": {
"Deleted": true
},
"ID": 108
}
}
Human Readable Output
Soft-Deleted Ticket #108
14. Search tickets
Returns a list of all tickets that match the filter criteria. If no filters are specified, all tickets from the last 30 days are returned.
Note that this command can consume multiple API credits. This can occur if the count of tickets resulting from your query exceeds 30. In that instance this command makes additional calls to the API to retrieve additional tickets matching your query.
Base Command
fd-search-tickets
Input
| Argument Name | Description | Required |
|---|---|---|
| filter | Predefined filters for filtering tickets. The options are: ‘new_and_my_open’ - New and my open tickets. ‘watching’ - Tickets I’m watching. ‘spam’ - Tickets that have been marked as spam. ‘deleted’ - Tickets that have been soft-deleted, aka moved to Trash. |
Optional |
| requester | Filter results by the ticket requester’s email address or ID. To find your contacts’ ID numbers or email addresses, run the fd-list-contacts command. |
Optional |
| updated_since | By default, tickets created within the previous 30 days are returned. For older tickets, use this filter (“2015-01-19T02:00:00Z”) | Optional |
| order_by | Field for ordering the list of tickets. The default sort order uses the ‘created_at’ field. | Optional |
| order_type | Return list results in ascending or descending order according to the order_by value, default is descending | Optional |
| include_stats | If set to ‘true’ then ticket’s closed_at, resolved_at and first_responded_at time will be included. Note that this is not set by default because setting this to ‘true’ will consume an additional 2 API credits per API call. To see more details, see the Freshdesk API documentation. | Optional |
| include_requester | If set to ‘true’ then the ticket requester’s ID, email address, mobile number, name, and phone number are included in the ticket’s output for each ticket. Note that this is not set by default because setting this to ‘true’ will consume an additional 2 API credits per API call. To see more details, see the Freshdesk API documentation. | Optional |
| include_description | If set to ‘true’ then the ticket’s description and description_text are included the tickets’ outputs. Note that this is not set by default because setting this to ‘true’ will consume an additional 2 API credits per API call. To see more details, see the Freshdesk API documentation. | Optional |
| custom_query | Filter tickets using a custom query. Format - "(ticket_field:integer OR ticket_field:‘string’) AND ticket_field:boolean" Example - "(type:‘Question’ OR type:‘Problem’) AND (due_by:>‘2017-10-01’ AND due_by:<‘2017-10-07’)" For more examples, see the Freshdesk API documentation. Note that the custom_query argument cannot be used in conjunction with this command’s other arguments. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Freshdesk.Ticket.ID | Number | ID number of the fetched ticket |
| Freshdesk.Ticket.Priority | Number | Ticket priority |
| Freshdesk.Ticket.DueBy | Date | Timestamp that denotes when the ticket is due to be resolved |
| Freshdesk.Ticket.Subject | String | Ticket subject |
| Freshdesk.Ticket.Status | Number | Ticket status |
| Freshdesk.Ticket.RequesterID | Number | User ID of the requester |
| Freshdesk.Ticket.Tag | Unknown | Tags associated with the ticket |
| Freshdesk.Ticket.GroupID | Number | ID of the group the ticket was assigned to |
| Freshdesk.Ticket.Source | Number | The channel through which the ticket was created |
| Freshdesk.Ticket.CreatedAt | Date | Ticket creation timestamp |
| Freshdesk.Ticket.ResponderID | Number | ID of the agent the ticket was assigned to |
| Freshdesk.Ticket.FrDueBy | Date | Timestamp that denotes when the first response is due |
| Freshdesk.Ticket.Conversation | Unknown | Conversations associated with this ticket |
| Freshdesk.Ticket.Attachment.AttachmentURL | Unknown | URL to download the file attached to the ticket to your local machine |
| Freshdesk.Ticket.Attachment.Name | Unknown | The name of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ContentType | String | Content type of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.ID | Unknown | ID number of the file attached to the ticket |
| Freshdesk.Ticket.Attachment.Size | String | Size of the file attached to the ticket |
Command Example
!fd-search-tickets updated_since=2019-02-05T08:30:00Z
Context Example
{
"Freshdesk.Ticket": [
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-06T13:30:16Z",
"FrDueBy": "2019-02-05T14:30:16Z",
"GroupID": null,
"Priority": 3,
"Source": 2,
"Tag": [
"new",
"attention needed",
"billing related"
],
"RequesterID": 2043024010476,
"UpdatedAt": "2019-02-05T14:32:44Z",
"AdditionalFields": {},
"ID": 105,
"CreatedAt": "2019-02-05T10:30:16Z",
"Subject": "Demonstrate Ticket Creation"
},
{
"Status": 2,
"ResponderID": 2043022085976,
"DueBy": "2019-02-05T11:05:48Z",
"FrDueBy": "2019-02-05T08:05:50Z",
"GroupID": 2043000867326,
"Priority": 4,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T13:07:54Z",
"AdditionalFields": {},
"ID": 97,
"CreatedAt": "2019-02-05T07:05:48Z",
"Subject": "Testing Ticket Update"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T10:00:00Z",
"FrDueBy": "2019-02-05T07:00:00Z",
"GroupID": 2043000867326,
"Priority": 4,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T12:08:05Z",
"AdditionalFields": {},
"ID": 96,
"CreatedAt": "2019-02-04T21:09:31Z",
"Subject": "Testing Ticket Update"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T10:00:00Z",
"FrDueBy": "2019-02-05T07:00:00Z",
"GroupID": 2043000867326,
"Priority": 4,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T12:08:03Z",
"AdditionalFields": {},
"ID": 95,
"CreatedAt": "2019-02-04T21:03:46Z",
"Subject": "Testing Ticket Update"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-07T15:00:00Z",
"FrDueBy": "2019-02-05T15:00:00Z",
"GroupID": null,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T15:00:49Z",
"AdditionalFields": {},
"ID": 94,
"CreatedAt": "2019-02-04T21:00:41Z",
"Subject": "Testing Ticket Creation"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-07T15:00:00Z",
"FrDueBy": "2019-02-05T15:00:00Z",
"GroupID": null,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T15:00:48Z",
"AdditionalFields": {},
"ID": 93,
"CreatedAt": "2019-02-04T20:19:19Z",
"Subject": "Testing Ticket Creation"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T13:20:08Z",
"FrDueBy": "2019-02-01T13:20:08Z",
"GroupID": null,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T13:33:04Z",
"AdditionalFields": {},
"ID": 61,
"CreatedAt": "2019-01-31T13:20:08Z",
"Subject": "Testing Ticket Creation"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T12:56:02Z",
"FrDueBy": "2019-02-01T12:56:02Z",
"GroupID": 2043000867326,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T15:08:34Z",
"AdditionalFields": {},
"ID": 60,
"CreatedAt": "2019-01-31T12:56:02Z",
"Subject": "Testing Ticket Creation"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T12:53:29Z",
"FrDueBy": "2019-02-01T12:53:29Z",
"GroupID": 2043000867326,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023528657,
"UpdatedAt": "2019-02-05T15:08:33Z",
"AdditionalFields": {},
"ID": 59,
"CreatedAt": "2019-01-31T12:53:29Z",
"Subject": "Lets see RawJson"
},
{
"Status": 2,
"ResponderID": null,
"DueBy": "2019-02-05T12:51:35Z",
"FrDueBy": "2019-02-01T12:51:35Z",
"GroupID": 2043000867326,
"Priority": 1,
"Source": 2,
"Tag": [],
"RequesterID": 2043023521956,
"UpdatedAt": "2019-02-05T15:08:33Z",
"AdditionalFields": {},
"ID": 58,
"CreatedAt": "2019-01-31T12:51:35Z",
"Subject": "Testing Ticket Creation"
}
]
}
Human Readable Output
Viewing All Requested Tickets
| ID | Priority | Status | Subject | DueBy | FrDueBy | RequesterID | GroupID | Source | CreatedAt | UpdatedAt | Tag |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 105 | 3 | 2 | Demonstrate Ticket Creation | 2019-02-06T13:30:16Z | 2019-02-05T14:30:16Z | 2043024010476 | 2 | 2019-02-05T10:30:16Z | 2019-02-05T14:32:44Z | new, attention needed, billing related |
|
| 97 | 4 | 2 | Testing Ticket Update | 2019-02-05T11:05:48Z | 2019-02-05T08:05:50Z | 2043023521956 | 2043000867326 | 2 | 2019-02-05T07:05:48Z | 2019-02-05T13:07:54Z | |
| 96 | 4 | 2 | Testing Ticket Update | 2019-02-05T10:00:00Z | 2019-02-05T07:00:00Z | 2043023521956 | 2043000867326 | 2 | 2019-02-04T21:09:31Z | 2019-02-05T12:08:05Z | |
| 95 | 4 | 2 | Testing Ticket Update | 2019-02-05T10:00:00Z | 2019-02-05T07:00:00Z | 2043023521956 | 2043000867326 | 2 | 2019-02-04T21:03:46Z | 2019-02-05T12:08:03Z | |
| 94 | 1 | 2 | Testing Ticket Creation | 2019-02-07T15:00:00Z | 2019-02-05T15:00:00Z | 2043023521956 | 2 | 2019-02-04T21:00:41Z | 2019-02-05T15:00:49Z | ||
| 93 | 1 | 2 | Testing Ticket Creation | 2019-02-07T15:00:00Z | 2019-02-05T15:00:00Z | 2043023521956 | 2 | 2019-02-04T20:19:19Z | 2019-02-05T15:00:48Z | ||
| 61 | 1 | 2 | Testing Ticket Creation | 2019-02-05T13:20:08Z | 2019-02-01T13:20:08Z | 2043023521956 | 2 | 2019-01-31T13:20:08Z | 2019-02-05T13:33:04Z | ||
| 60 | 1 | 2 | Testing Ticket Creation | 2019-02-05T12:56:02Z | 2019-02-01T12:56:02Z | 2043023521956 | 2043000867326 | 2 | 2019-01-31T12:56:02Z | 2019-02-05T15:08:34Z | |
| 59 | 1 | 2 | Lets see RawJson | 2019-02-05T12:53:29Z | 2019-02-01T12:53:29Z | 2043023528657 | 2043000867326 | 2 | 2019-01-31T12:53:29Z | 2019-02-05T15:08:33Z | |
| 58 | 1 | 2 | Testing Ticket Creation | 2019-02-05T12:51:35Z | 2019-02-01T12:51:35Z | 2043023521956 | 2043000867326 | 2 | 2019-01-31T12:51:35Z | 2019-02-05T15:08:33Z |
Configuration parameters
url— Server URL (e.g., https://demistohelp.freshdesk.com) (required)token— API Token. (You must enter either the API token or your Freshdesk credentials)token_creds—insecure— Trust any certificate (not secure)proxy— Use system proxy settingsfetch_time— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)credentials— UsernameisFetch— Fetch incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalmaxFetch— Maximum number of incidents per fetch.
Commands (14)
-
fd-create-ticketCreates a new Freshdesk ticket.
-
fd-create-ticket-noteCreates a note for a specified ticket. By default, notes are private. To make a note public, set the 'private' argument to false.
-
fd-delete-ticketDeletes a ticket.
-
fd-get-canned-response-folderView the details of all the Canned Responses in a Folder.
-
fd-get-contactView the details of the contact specified by the ID number.
-
fd-get-ticketGets details of a ticket, specified by the ticket ID number.
-
fd-get-ticket-conversationsLists all replies and notes for a specified ticket.
-
fd-list-agentsReturns a list of agents that match the filter criteria.
-
fd-list-canned-response-foldersList all Canned Response Folders (Only users with Admin Privileges).
-
fd-list-contactsList all contacts matching the specified filters. If no filters are provided then all unblocked and undeleted contacts will be returned.
-
fd-list-groupsLists all groups.
-
fd-search-ticketsReturns a list of all tickets that match the filter criteria. If no filters are specified, all tickets from the last 30 days are returned. Note that this command can consume multiple API credits - this can occur if the count of tickets resulting from your query exceeds 30 - in that instance this command makes additional calls to the API to retrieve additional tickets matching your query.
-
fd-ticket-replyAdds a reply to a specified ticket.
-
fd-update-ticketUpdate the ticket specified by the ID.
import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * """ IMPORTS """ import requests import urllib3 # Disable insecure warnings urllib3.disable_warnings() # Remove proxy if not set to true in params handle_proxy(proxy_param_name="proxy") """ GLOBALS/PARAMS """ PARAMS = demisto.params() CREDS = PARAMS.get("credentials") USERNAME = CREDS.get("identifier") if CREDS else None PASSWORD = CREDS.get("password") if CREDS else None TOKEN = PARAMS.get("token_creds", {}).get("password") or PARAMS.get("token") if not (USERNAME and PASSWORD) and not TOKEN: err_msg = ( "You must provide either your Freshdesk account API key or the " "username and password you use to sign into your Freshdesk account " "when instantiating an instance of the Freshdesk integration." ) return_error(err_msg) AUTH = (TOKEN, "X") if TOKEN else (USERNAME, PASSWORD) # How much time before the first fetch to retrieve incidents FETCH_TIME = PARAMS.get("fetch_time", "24 hours") # Remove trailing slash to prevent wrong URL path to service SERVER = PARAMS["url"].removesuffix("/") # Should we use SSL USE_SSL = not PARAMS.get("insecure", False) # Service base URL BASE_URL = SERVER + "/api/v2/" # Headers to be sent in requests HEADERS = {"Content-Type": "application/json", "Accept": "application/json"} # Headers to be used when making a request to POST a multi-part encoded file MULTIPART_HEADERS = {"Accept": "application/json"} # Amount of results returned per fetch (default 50) MAX_INCIDENTS = int(PARAMS.get("maxFetch", 50)) # Default amount of results returned per-page/per-api-call when the # fd-search-tickets command's results that match the command's specified # filter criteria exceeds 30 PER_PAGE = 30 # The API response ticket attributes that will be included # in most command's context outputs DEFAULT_TICKET_CONTEXT_FIELDS = [ "priority", "due_by", "subject", "status", "requester_id", "tags", "group_id", "source", "created_at", "responder_id", "fr_due_by", "id", ] """ HELPER FUNCTIONS """ def get_number_of_incidents_to_fetch(): # FreshDesk API supports maximum of 100 tickets per page so if user asked for more, pagination is needed. return 100 if MAX_INCIDENTS >= 100 else MAX_INCIDENTS def reformat_canned_response_context(context): """ Reformat context for canned-response related commands (from having used string_to_context_key) to desired output format. parameter: (dict) context The context to reformat returns: The reformatted context """ for key, val in list(context.items()): if "Id" in key: new_key = key.replace("Id", "ID") context[new_key] = val del context[key] elif "Html" in key: new_key = key.replace("Html", "HTML") context[new_key] = val del context[key] return context def reformat_conversation_context(context): """ Reformat context for conversation related commands (from having used string_to_context_key) to desired output format. parameter: (dict) context The context to reformat returns: The reformatted context """ to_emails = context.get("ToEmails") body = context.get("Body") attachments = context.get("Attachments") if to_emails: context["ToEmail"] = to_emails del context["ToEmails"] if body: context["BodyHTML"] = body del context["Body"] if attachments: del context["Attachments"] return context def format_contact_context(contact): """ Format context for contact related commands. parameter: (dict) contact The API response from executing a contact related command whose attributes need to be parsed into context returns: The formatted context """ dont_include = ["other_companies", "other_emails", "custom_fields", "avatar"] # Parse response into context context = {} for key, val in contact.items(): if key not in dont_include and val: new_key = string_to_context_key(key) if "Id" in new_key: new_key = new_key.replace("Id", "ID") elif new_key == "Tags": new_key = "Tag" context[new_key] = val return context def reformat_ticket_context(context): """ Reformat context outputs (from having used string_to_context_key) to desired output format. parameter: (dict) context The context to reformat returns: The reformatted context """ standard_context_outputs = [ "Priority", "DueBy", "Subject", "Status", "RequesterID", "Tag", "GroupID", "Source", "CreatedAt", "ResponderID", "FrDueBy", "ID", "UpdatedAt", "Attachment", "AdditionalFields", "UserID", "BodyText", "Category", "Private", "Incoming", ] for key, val in list(context.items()): if key == "Tags": new_key = key[:-1] context[new_key] = val del context[key] elif key == "CustomFields": new_key = key[:-1] context[new_key] = val del context[key] elif key == "FwdEmails": new_key = key[:-1] context[new_key] = val del context[key] elif key == "Description": new_key = "DescriptionHTML" context[new_key] = val del context[key] elif "Id" in key: new_key = key.replace("Id", "ID") context[new_key] = val del context[key] elif "Cc" in key: new_key = key.removesuffix("s").replace("Cc", "CC") context[new_key] = val del context[key] # If 'Attachments' are still in context get rid of them - should be 'Attachment' attachments = context.get("Attachments") if attachments: del context["Attachments"] new_context = {} new_context["AdditionalFields"] = context.get("AdditionalFields") if context.get("AdditionalFields") else {} additional_fields = {} for key, val in list(context.items()): if key not in standard_context_outputs: if not ((isinstance(val, dict | list)) and len(val) == 0): additional_fields[key] = val else: new_context[key] = val new_context["AdditionalFields"] = dict(new_context.get("AdditionalFields", {}), **additional_fields) return new_context def handle_search_tickets_pagination(args, response, limit=-1): """ Retrieve all resulting tickets even over the default 30 returned by a single API call. When the search_tickets_command results in more tickets than the default per page count (30) returned from making an API call, then this function retrieves the remaining tickets by iterating and making API calls per additional page of results. parameter: (dict) args search_tickets_command arguments parameter: (dict) response The initial json response from making an API call in the search_tickets function parameter: (int) limit Stops the pagination as soon as the number of tickets exceeds the limit (default -1 for no limit) returns: All Ticket Objects """ # If user entered custom_query arg, the resulting tickets are in the 'results' attribute of the response if args.get("custom_query"): # Max page count allowed by API when using custom query max_pages = 10 # Deal with pagination if necessary tickets = response.get("results") total_tickets = response.get("total") total_tickets -= PER_PAGE page = 1 while total_tickets > 0 and page <= max_pages: page += 1 args["page"] = page tickets_page = search_tickets(args) tickets.extend(tickets_page.get("results")) total_tickets -= PER_PAGE else: # Max page count allowed by API when using normal filters max_pages = 300 tickets = response page = 1 next_page = tickets while next_page and page <= max_pages: # Stop pagination if limit is defined and we exceeded it if 0 < limit <= len(tickets): break page += 1 args["page"] = page next_page = search_tickets(args) if next_page: tickets.extend(next_page) return tickets def attachments_into_context(api_response, context): """ Get the attachments field from the api_response argument if present and parse it into the context. parameter: (dict) api_response The json response returned by the calling function's associated 'requests' function in which calls to the API are made. parameter: (dict) context The context that will be modified and returned to the war room returns: The modified context, and the modified context with the attachments in readable format for the human readable output """ attachment_keys_to_include = ["attachment_url", "content_type", "id", "name", "size"] context_readable = dict(**context) # Parse attachments into context attachments = api_response.get("attachments") if attachments: attachments_context = [] attachments_context_readable = [] for attachment in attachments: attachment_context = {} for key, val in attachment.items(): if key in attachment_keys_to_include: if key == "attachment_url": key = "AttachmentURL" elif key == "id": key = "ID" else: key = string_to_context_key(key) attachment_context[key] = val attachment_formatted = formatCell(attachment_context).split("\n") attachment_formatted = ", ".join(attachment_formatted) attachments_context_readable.append(attachment_formatted) attachments_context.append(attachment_context) context["Attachment"] = attachments_context context_readable["Attachment"] = attachments_context_readable return context, context_readable def additional_fields_to_context(context, already_in_context, additional_fields, additional_values): """ Parses fields not presented as part of the command's standard arguments into the context. For commands where the user can enter additional fields and their associated values beyond what is offered by the standard arguments (but are still supported by the API endpoint). If the additional fields are not part of the standard context output for that command, then those fields and values from the API call are parsed and subcategorized under the 'AdditionalFields' context output. parameter: (dict) context The context that will be modified and returned to the war room parameter: (list) already_in_context List of fields which are ordinarily/already parsed into the context parameter: (list) additional_fields List of the fields beyond the command's standard arguments that the user entered as part of the call to the API endpoint parameter: (list) additional_values List of values corresponding to the additional_fields argument returns: The modified context """ # Parse additional fields into context if additional_fields and additional_values: added_context = {} for field, value in zip(additional_fields, additional_values): if field not in already_in_context and field != "attachments": key = string_to_context_key(field) added_context[key] = value context["AdditionalFields"] = added_context return context def additional_fields_to_args(args, additional_fields_arg_name): """ Parses the additional_fields command argument for the individual fields and values and reassigns them to the args dictionary. parameter: (dict) args The command's arguments parameter: (string) additional_fields_arg_name The name of the command argument that contains the additional fields and values returns: The args dictionary that has been updated with the additional fields and values, the list of additional fields, and the list of additional values. """ additional_fields = args.get(additional_fields_arg_name) if additional_fields: fields, values = [], [] fields_and_vals = additional_fields.split(";") # For the case there is only one additional field + value if len(fields_and_vals) == 1: fields_and_vals = list(fields_and_vals) for field_and_val in fields_and_vals: field_and_val = field_and_val.split("=") # If the length doesn't equal 2, means there were either no equal signs or more than one if len(field_and_val) != 2: err_msg = "It appears you entered either too many or too few equal signs in the 'additional_fields' argument." return_error(err_msg) field = field_and_val[0].strip() val = field_and_val[1] # If the value contains commas, then it is a list if "," in val: val = argToList(val) args[field] = val fields.append(field) values.append(val) del args[additional_fields_arg_name] return args, fields, values return args, None, None def ticket_to_incident(ticket): """ Create incident from ticket object. parameter: (object) ticket Ticket object returns: Incident Object """ incident = {} # Incident Title subject = ticket.get("subject", "").encode("ascii", "replace").decode("utf-8") incident["name"] = f'Freshdesk Ticket: "{subject}"' # Incident update time - the ticket's update time - The API does not support filtering tickets by creation time # but only by update time. The update time will be the creation time of the incidents and the incident id check will # prevent duplications of incidents. incident["occurred"] = ticket.get("updated_at") # The raw response from the service, providing full info regarding the item incident["rawJSON"] = json.dumps(ticket) return incident def get_additional_fields(args): """ Determine which fields need to be added to context based off arguments given in the search_tickets_command. parameter: (dict) args The search_tickets_command arguments returns: List of fields to be added to the context outputs """ additional_fields = [] # fields that should be added to output context filter = args.get("filter") if filter: if filter == "deleted": additional_fields.append("deleted") elif filter == "spam": additional_fields.append("spam") requester = args.get("requester") if requester and "@" in requester: additional_fields.append("email") company_id = args.get("company_id") if company_id: additional_fields.append("company_id") if args.get("include_description") and args.get("include_description").lower() == "yes": additional_fields.extend(["description", "description_text"]) return additional_fields def entries_to_files(entry_ids): """ Format file details (retrieved using the files' entry IDs) to API expectations to include files in API call. parameter: (list) entry_ids List of entry ID strings for files uploaded to the warroom returns: List of attachment field, value tuples formatted according to API expectations """ attachments = [] for entry_id in entry_ids: execute_results = demisto.getFilePath(entry_id) file_path = execute_results["path"] file_name = execute_results["name"] attachments.append(("attachments[]", (file_name, open(file_path, "rb")))) return attachments def handle_array_input(args): """ Format any command argument that is supposed to be an array from a string to a list. parameter: (dict) args The command arguments dictionary returns: The arguments dict with field values transformed from strings to lists where necessary """ array_inputs = [ "tags", "attachments", "cc_emails", "bcc_emails", "to_emails", "update_fields", "update_values", "notify_emails", ] attchs_present = args.get("attachments") if attchs_present: for arr_input in array_inputs: if arr_input in args: if arr_input != "attachments": args[arr_input + "[]"] = argToList(args.get(arr_input)) del args[arr_input] else: args[arr_input] = argToList(args.get(arr_input)) else: for arr_input in array_inputs: if arr_input in args: args[arr_input] = argToList(args.get(arr_input)) return args def validate_priority_input(args): """ Check entered value for command argument 'priority' and format to API expectations. parameter: (dict) args The command arguments dictionary returns: The arguments dict with the value for 'priority' field reformatted if necessary """ # Parse and ensure valid command argument priority = args.get("priority", None) # If priority wasn't given by the user as a cmd arg # then no need to alter it to API expectations if not priority: return args priorities = ["low", "medium", "high", "urgent"] # Check if the user entered status as words - aka the # options listed above in 'statuses' err_msg = "priority should be one of these values: 1, 2, 3, 4, {}".format(", ".join(priorities)) if len(priority) > 1: if priority.lower() in priorities: # Add 1 since API status numbers for tickets start at 1 # Cast to string so clean_arguments helper function doesn't throw any errors args["priority"] = str(priorities.index(priority.lower()) + 1) else: return_error(err_msg) # Otherwise make sure the user entered valid status number elif not (0 < int(priority) < 5): return_error(err_msg) return args def validate_status_input(args): """ Check entered value for command argument 'status' and format to API expectations. parameter: (dict) args The command arguments dictionary returns: The arguments dict with the value for 'status' field reformatted if necessary """ # Parse and ensure valid command argument status = args.get("status", None) # If status wasn't given by the user as a cmd arg # then no need to alter it to API expectations if not status: return args statuses = ["open", "pending", "resolved", "closed", "waiting on customer", "waiting on third party"] # Check if the user entered status as words - aka the # options listed above in 'statuses' err_msg = "status should be one of these values: 2, 3, 4, 5, 6, 7, {}".format(", ".join(statuses)) if len(status) > 1: if status.lower() in statuses: # Add 2 since API status numbers for tickets start at 2 # Cast to string so clean_arguments helper function doesn't throw any errors args["status"] = str(statuses.index(status.lower()) + 2) else: return_error(err_msg) # Otherwise make sure the user entered valid status number elif not (1 < int(status) < 8): return_error(err_msg) return args def handle_number_input(args): """ Format any command argument that is supposed to be a number from a string to an int. parameter: (dict) args The command arguments dictionary returns: The arguments dict with field values transformed from strings to numbers where necessary """ # Command args that should be numbers number_args = [ "requester_id", "status", "priority", "responder_id", "email_config_id", "group_id", "product_id", "source", "company_id", ] # Convert cmd args that are expected to be numbers from strings to numbers for num_arg in number_args: if num_arg in args: args[num_arg] = int(args.get(num_arg)) return args def clean_arguments(args): """ Perform all validation and reformatting of command arguments. parameter: (dict) args The command arguments dictionary returns: The arguments dict with all field values reformatted where necessary """ args = validate_status_input(args) args = validate_priority_input(args) args = handle_array_input(args) args = handle_number_input(args) return args def determine_identifier(args): """ Determine whether the input for the 'identifier' argument is an email or twitter handle and adjust 'args' accordingly. parameter: (dict) args The command arguments dictionary returns: The arguments dict with the email or twitter_id field (depending on what the value was) assigned the value entered for the 'identifier' argument """ identifier = args.get("identifier") if identifier.startswith("@"): # Then it's a twitter handle args["twitter_id"] = identifier elif "@" in identifier: # Otherwise assume it's an email address args["email"] = identifier else: err_msg = "The entered value for the 'identifier' argument must be either a Twitter handle or an Email Address." return_error(err_msg) # Delete identifier field from args since it doesn't match API expected inputs del args["identifier"] return args def determine_responder(args): """ Determine whether the input for the 'responder' argument is a group or an agent and adjust 'args' accordingly. parameter: (dict) args The command arguments dictionary returns: The arguments dict with the group_id or responder_id field (depending on what the value was) assigned the value entered for the 'responder' argument """ responder = args.get("responder", None) if responder: args = determine_group(args, "responder") args = determine_agent(args, "responder") return args def determine_agent(args, key_name): """ Determine if the value points to an agent by checking against all agent names, emails, and IDs, and adjust 'args' accordingly. parameter: (string) key_name The name of the command argument parameter: (dict) args The command arguments dictionary returns: The arguments dict with responder_id field assigned the appropriate value if the value passed for the command argument represented by 'key_name' is associated with an Agent's details """ assigned_agent = args.get(key_name, None) if assigned_agent: agent_emails, agent_names, agent_ids = [], [], [] # Get names, emails and ids of agents agents = list_agents() for agent in agents: agent_ids.append(agent.get("id")) contact_info = agent.get("contact") if contact_info: agent_names.append(contact_info.get("name", "").lower()) agent_emails.append(contact_info.get("email", "").lower()) # Check if responder value is a contact ID if assigned_agent in agent_ids: args["responder_id"] = assigned_agent # Delete assigned_agent field from args since it doesn't match API expected inputs del args[key_name] elif assigned_agent.lower() in agent_names: agent_name_idx = agent_names.index(assigned_agent.lower()) args["responder_id"] = agent_ids[agent_name_idx] del args[key_name] elif assigned_agent.lower() in agent_emails: agent_email_idx = agent_emails.index(assigned_agent.lower()) args["responder_id"] = agent_ids[agent_email_idx] del args[key_name] return args def determine_group(args, key_name): """ Determine if the value points to a group by checking against all group names and IDs, and adjust 'args' accordingly. parameter: (string) key_name The name of the command argument parameter: (dict) args The command arguments dictionary returns: The arguments dict with group_id field assigned the appropriate value if the value passed for the command argument represented by 'key_name' is associated with a Group's details """ assigned_group = args.get(key_name, None) if assigned_group: group_names, group_ids = [], [] # Get names and ids of groups groups = list_groups() for group in groups: group_ids.append(group.get("id")) group_names.append(group.get("name", "").lower()) # Check if responder value is a group ID if assigned_group in group_ids: args["group_id"] = assigned_group del args[key_name] # Or the name of a group elif assigned_group.lower() in group_names: group_name_idx = group_names.index(assigned_group.lower()) args["group_id"] = group_ids[group_name_idx] del args[key_name] return args def http_request(method, url_suffix, params=None, data=None, files=None, headers=HEADERS): """ A wrapper for requests lib to send our requests and handle requests and responses better. parameter: (string) method A string denoting the http request method to use. Can be 'GET', 'POST, 'PUT', 'DELETE', etc. parameter: (string) url_suffix The API endpoint that determines which data we are trying to access/change in our call to the API parameter: (dict) params The key/value pairs to be encoded as part of the URL's query string parameter: (dict) data The key/value pairs to be form-encoded parameter: (list) files The multipart-encoded files to upload parameter: (dict) headers The headers to use with the request returns: JSON Response Object """ res = requests.request( method, BASE_URL + url_suffix, verify=USE_SSL, params=params, data=data, headers=headers, auth=AUTH, # type: ignore[arg-type] files=files, ) # Handle error responses gracefully if res.status_code not in {200, 201, 202, 204}: LOG(res.json()) LOG(res.text) LOG.print_log() err_msg = f"Error in API call to Freshdesk Integration [{res.status_code}] - {res.reason}" err = json.loads(res.content) if err.get("errors"): for error in err.get("errors"): err_msg += "\n" + json.dumps(error, indent=2) else: for key, value in res.json().items(): err_msg += f"\n{key}: {value}" return_error(err_msg) # Handle response with no content elif res.status_code == 204: return res return res.json() """ COMMANDS + REQUESTS FUNCTIONS """ def test_module(): """ Will try to make a request to the API endpoint for listing all tickets. """ http_request("GET", "tickets") def fetch_incidents(): per_page = get_number_of_incidents_to_fetch() # demisto.getLastRun() will returns an obj with the previous run in it. last_run = demisto.getLastRun() # Get the last fetch time and last id fetched if exist last_fetch = last_run.get("last_created_incident_timestamp") last_incident_id = last_run.get("last_incident_id", -1) # Handle first time fetch, fetch incidents retroactively if not last_fetch: last_fetch, _ = parse_date_range(FETCH_TIME, to_timestamp=True) updated_since = timestamp_to_datestring(last_fetch, date_format="%Y-%m-%dT%H:%M:%SZ") args = {"updated_since": updated_since, "order_type": "asc", "per_page": per_page} response = search_tickets(args) # page 1 # handle pagination until user's limit tickets = handle_search_tickets_pagination(args, response, limit=MAX_INCIDENTS) # convert the ticket/events to demisto incidents incidents = [] for ticket in tickets: incident = ticket_to_incident(ticket) incident_id = ticket.get("id") incident_date = date_to_timestamp(incident.get("occurred"), "%Y-%m-%dT%H:%M:%SZ") # Update last run and add incident if the incident is newer than last fetch and was not fetched before # The incident IDs are in incremental order. if incident_date >= last_fetch and incident_id > last_incident_id: last_fetch = incident_date incidents.append(incident) last_incident_id = incident_id if len(incidents) >= MAX_INCIDENTS: break demisto.setLastRun({"last_created_incident_timestamp": last_fetch, "last_incident_id": last_incident_id}) demisto.incidents(incidents) """<------ TICKETS ------>""" def create_ticket(args): args = determine_identifier(args) args = determine_responder(args) args = clean_arguments(args) endpoint_url = "tickets" response = None if not args.get("attachments"): # The service endpoint to request from # Send a request using our http_request wrapper response = http_request("POST", endpoint_url, data=json.dumps(args)) else: # Get the files from their entry IDs attachments = entries_to_files(args.get("attachments")) # Format to API expectations del args["attachments"] # Send a request and get raw response response = http_request("POST", endpoint_url, data=args, files=attachments, headers=MULTIPART_HEADERS) return response def create_ticket_command(): """ Create a new Freshdesk ticket. demisto parameter: (string) subject Subject of the ticket. The default Value is null. demisto parameter: (string) description Details of the issue for which you are creating a ticket. demisto parameter: (number) priority Priority of the ticket. Each number has a corresponding value. 1 is Low, 2 is Medium, 3 is High, 4 is Urgent. demisto parameter: (number) status Status of the ticket. Each number has a corresponding value. 2 is Open, 3 is Pending, 4 is Resolved, 5 is Closed, 6 is Waiting on Customer, 7 is Waiting on Third Party. demisto parameter: (string) identifier This can be an email address or a twitter handle demisto parameter: (list) responder ID or name of the group or agent to whom you wish to assign this ticket. To find potential assignees, try executing the fd-list-groups command. demisto parameter: (list) attachments Entry IDs of files to attach to the ticket. The total size of these attachments cannot exceed 15MB. demisto parameter: (list) additional_fields Additional ticket fields you wish to set the value of returns: Ticket Object """ # Get command arguments from user args = demisto.args() # Handle additional_fields command arguments args, additional_fields, additional_values = additional_fields_to_args(args, "additional_fields") # Make request and get raw response ticket = create_ticket(args) # Parse response into context include_in_context = DEFAULT_TICKET_CONTEXT_FIELDS[:] context = {string_to_context_key(key): val for key, val in ticket.items() if val} context = additional_fields_to_context(context, include_in_context, additional_fields, additional_values) context, context_readable = attachments_into_context(ticket, context) context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) title = "Newly Created Ticket #{}".format(context.get("ID")) human_readable = tableToMarkdown(title, context_readable, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": ticket, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": context}, } ) def update_ticket(args): # Get ticket number ticket_number = args.get("id") # Remove ticket number from args del args["id"] args = determine_agent(args, "assigned_agent") args = determine_group(args, "assigned_group") args = clean_arguments(args) # The service endpoint to request from endpoint_url = f"tickets/{ticket_number}" response = None if not args.get("attachments"): # Send a request using our http_request wrapper response = http_request("PUT", endpoint_url, data=json.dumps(args)) else: # Get the files from their entry IDs attachments = entries_to_files(args.get("attachments")) # Format to API expectations del args["attachments"] # Send a request and get raw response response = http_request("PUT", endpoint_url, data=args, files=attachments, headers=MULTIPART_HEADERS) return response def update_ticket_command(): """ Update the ticket specified by a ticket ID number. demisto parameter: (string) subject Update the ticket's subject field demisto parameter: (number,string) status Update the ticket's status. Possible values are 2,3,4,5,6,7 or 'Open' , 'Pending', 'Resolved', 'Closed', 'Waiting on Customer', 'Waiting on Third Party' demisto parameter: (number,string) priority Update the ticket's priority. Possible values are 1,2,3,4 or 'Low', 'Medium', 'High', 'Urgent' demisto parameter: (string) description The HTML content of the ticket demisto parameter: (number) id ID number of the ticket to update demisto parameter: assigned_agent Update which agent is assigned to respond to this ticket. Values can be either the agent's ID number, name, or email. demisto parameter: assigned_group Update which group is assigned to respond to this ticket. Values can be either the group's ID number or name. demisto parameter: (list) additional_fields Fields not included in the default command arguments that you wish to enter the value for returns: Ticket specified by the ticket ID number with its updated values """ args = demisto.args() args, additional_fields, additional_fields_values = additional_fields_to_args(args, "additional_fields") # Make request and get raw response ticket = update_ticket(args) # Parse response into context include_in_context = DEFAULT_TICKET_CONTEXT_FIELDS[:] include_in_context.append("updated_at") # Parse default context fields context = {string_to_context_key(key): val for key, val in ticket.items() if val} # Parse additional fields into context context = additional_fields_to_context(context, include_in_context, additional_fields, additional_fields_values) # Parse attachments into context context, context_readable = attachments_into_context(ticket, context) context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) title = "Ticket #{} Updated".format(context.get("ID")) human_readable = tableToMarkdown(title, context_readable, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": ticket, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": context}, } ) def get_ticket(args): ticket_number = args.get("id") endpoint_url = f"tickets/{ticket_number}" url_params = {} # Check if embedding additional info in API response was specified in cmd args include = "" if args.get("include_stats") and args.get("include_stats").lower() == "true": include += "stats" if args.get("include_requester") and args.get("include_requester").lower() == "true": include += "requester" if include == "" else ", requester" if include != "": url_params["include"] = include response = http_request("GET", endpoint_url, params=url_params) return response def get_ticket_command(): """ View a Ticket. demisto parameter: (number) id ID number of the ticket to fetch demisto parameter: (string) include_requester If set to 'yes' then the ticket requester's id, email, mobile, name, and phone will be included in the ticket's output. demisto parameter: (string) include_stats If set to 'yes' then the ticket's closed_at, resolved_at and first_responded_at times will be included. returns: Ticket Object """ # Get command arguments from user args = demisto.args() # Make request and get raw response ticket = get_ticket(args) nonstd_context_fields = ["requester", "stats"] # Parse response into context context = { string_to_context_key(key): val for key, val in ticket.items() if key not in nonstd_context_fields and val is not None } # Parse attachments into context context, context_readable = attachments_into_context(ticket, context) context["AdditionalFields"] = {} requester = ticket.get("requester") if requester: requester_context = {string_to_context_key(key): val for key, val in requester.items() if val} context["AdditionalFields"]["Requestor"] = requester_context stats = ticket.get("stats") if stats: stats_context = {string_to_context_key(key): val for key, val in stats.items() if val} context["AdditionalFields"]["Stats"] = stats_context if not ticket.get("deleted"): context["AdditionalFields"]["Deleted"] = False context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) title = "Viewing Ticket #{}".format(ticket.get("id")) human_readable = tableToMarkdown(title, context_readable, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": ticket, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": context}, } ) def delete_ticket(ticket_id): endpoint_url = f"tickets/{ticket_id}" response = http_request("DELETE", endpoint_url) return response def delete_ticket_command(): """ Soft-Delete the ticket specified by the 'id' command argument. demisto parameter: (number) id ID of the ticket to delete returns: Success Message """ ticket_id = demisto.args().get("id") # Make request delete_ticket(ticket_id) ticket_context = {"ID": int(ticket_id), "AdditionalFields": {"Deleted": True}} message = f"Soft-Deleted Ticket #{ticket_id}" demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": ticket_context, "ReadableContentsFormat": formats["markdown"], "HumanReadable": message, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": ticket_context}, } ) def search_tickets(args): endpoint_url = "tickets" url_params = {} # Filter By filter = args.get("filter") if filter: url_params["filter"] = filter requester = args.get("requester") if requester: if "@" in requester: url_params["email"] = requester else: url_params["requester_id"] = requester updated_since = args.get("updated_since") if updated_since: url_params["updated_since"] = updated_since per_page = args.get("per_page") if per_page: url_params["per_page"] = per_page # Sort By order_by = args.get("order_by") if order_by: url_params["order_by"] = order_by order_type = args.get("order_type") if order_type: url_params["order_type"] = order_type # Embeddings (include additional information) include = "" if args.get("include_stats") and args.get("include_stats").lower() == "true": include += "stats" if args.get("include_requester") and args.get("include_requester").lower() == "true": include += "requester" if include == "" else ", requester" if args.get("include_description") and args.get("include_description").lower() == "true": include += "description" if include == "" else ", description" if include != "": url_params["include"] = include # Custom Query custom_query = args.get("custom_query") if custom_query and url_params: err_msg = ( "You cannot use the custom_query argument in conjunction with the other command arguments. You can " "either use the other arguments that allow you to choose options for filtering, sorting, " "and including information for tickets, or to use the custom_query alone to create a custom filter " "that determines which tickets are listed." ) return_error(err_msg) elif custom_query: endpoint_url = "search/tickets" url_params["query"] = '"' + custom_query + '"' page = args.get("page") if page: url_params["page"] = page # Make request and get raw response response = http_request("GET", endpoint_url, params=url_params) return response def search_tickets_command(): """ List all tickets that match the filter criteria you specify. demisto parameter: (string) filter Predefined filters demisto parameter: requester Filter by either the ticket requester's email or ID demisto parameter: (datetime) updated_since By default, only tickets that have been created within the past 30 days will be returned. For older tickets, use this filter. Example value for this field would be '2015-01-19T02:00:00Z' demisto parameter: (string) order_by Reference field for ordering the list of tickets. The default sort order is created_at. demisto parameter: (string) order_type Whether to order the resulting tickets in ascending or descending order. The default is descending. Value can be either 'asc' or 'desc'. demisto parameter: (string) include_stats If set to 'yes' then the ticket's closed_at, resolved_at and first_responded_at times will be included. demisto parameter: (string) include_requester If set to 'yes' then the ticket requester's id, email, mobile, name, and phone will be included in the ticket's output for each ticket. demisto parameter: (string) include_description If set to 'yes' then the ticket's description and description_text will be included the tickets' outputs. demisto parameter: (string) custom_query Filter tickets using a custom query. Format - "(ticket_field:integer OR ticket_field:'string') AND ticket_field:boolean" Example - "(type:'Question' OR type:'Problem') AND (due_by:>'2017-10-01' AND due_by:<'2017-10-07')" Note that the custom_query argument cannot be used in conjunction with this command's other arguments. returns: Ticket Objects """ args = demisto.args() additional_fields = get_additional_fields(args) response = search_tickets(args) # page 1 tickets = handle_search_tickets_pagination(args, response) context_outputs = DEFAULT_TICKET_CONTEXT_FIELDS[:] context_outputs.append("updated_at") # Parse response into context contexts = [] readable_contexts = [] for ticket in tickets: # Parse ticket into the standard outputs context = {string_to_context_key(key): val for key, val in ticket.items() if key in context_outputs} # Parse ticket attachments into context context, context_readable = attachments_into_context(ticket, context) # Parse ticket for the additionally requested fields context["AdditionalFields"] = {string_to_context_key(key): val for key, val in ticket.items() if key in additional_fields} requester = ticket.get("requester") if requester: requester_context = {string_to_context_key(key): val for key, val in requester.items() if val} context["AdditionalFields"]["Requestor"] = requester_context stats = ticket.get("stats") if stats: stats_context = {string_to_context_key(key): val for key, val in stats.items() if val} context["AdditionalFields"]["Stats"] = stats_context context_readable = reformat_ticket_context(context_readable) readable_contexts.append(context_readable) context = reformat_ticket_context(context) contexts.append(context) table_headers = [ "ID", "Priority", "Status", "Subject", "DueBy", "FrDueBy", "RequesterID", "GroupID", "Source", "CreatedAt", "UpdatedAt", "Tag", "AdditionalFields", "Attachment", ] title = "Viewing All Requested Tickets" human_readable = tableToMarkdown(title, readable_contexts, headers=table_headers, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": tickets, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": contexts}, } ) """<------ CONVERSATIONS ------>""" def ticket_reply(args): ticket_id = args.get("ticket_id") del args["ticket_id"] args = handle_array_input(args) endpoint_url = f"tickets/{ticket_id}/reply" response = None if not args.get("attachments"): # The service endpoint to request from # Send a request using our http_request wrapper response = http_request("POST", endpoint_url, data=json.dumps(args)) else: # Get the files from their entry IDs attachments = entries_to_files(args.get("attachments")) # Format to API expectations del args["attachments"] # Send a request and get raw response response = http_request("POST", endpoint_url, data=args, files=attachments, headers=MULTIPART_HEADERS) return response def ticket_reply_command(): """ Reply to a specified ticket. demisto parameter: (number) ticket_id ID of the ticket you wish to respond to demisto parameter: (string) body Content of the reply in HTML format demisto parameter: (string) from_email The email address from which the reply is sent. By default the global support email will be used. demisto parameter: (number) user_id ID of the agent who is adding the note demisto parameter: (list) cc_emails Array of email address strings added in the 'cc' field of the outgoing ticket email. demisto parameter: (list) bcc_emails Array of email address strings added in the 'bcc' field of the outgoing ticket email. demisto parameter: (list) attachments Entry IDs of files to attach to the reply. The total size of these attachments cannot exceed 15MB. returns: Ticket Reply Object """ args = demisto.args() # Make request and get raw response reply = ticket_reply(args) # Parse response into context context = {string_to_context_key(key): val for key, val in reply.items() if val} context = reformat_conversation_context(context) # Parse attachments into context context, context_readable = attachments_into_context(reply, context) context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) complete_context = {"ID": int(reply.get("ticket_id")), "Conversation": context} title = "Reply to Ticket #{}".format(reply.get("ticket_id")) human_readable = tableToMarkdown(title, context_readable, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": reply, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": complete_context}, } ) def create_ticket_note(args): ticket_id = args.get("ticket_id") del args["ticket_id"] args = handle_array_input(args) # Set defaults for 'private' and 'incoming' fields if not set by user args["private"] = args.get("private", "true") args["incoming"] = args.get("incoming", "false") endpoint_url = f"tickets/{ticket_id}/notes" response = None if not args.get("attachments"): # Format boolean args to API expectations dumped_args = json.dumps(args).replace('"false"', "false").replace('"true"', "true") # The service endpoint to request from # Send a request using our http_request wrapper response = http_request("POST", endpoint_url, data=dumped_args) else: # Get the files from their entry IDs attachments = entries_to_files(args.get("attachments")) # Format to API expectations del args["attachments"] # Send a request and get raw response response = http_request("POST", endpoint_url, data=args, files=attachments, headers=MULTIPART_HEADERS) return response def create_ticket_note_command(): """ Create a note for a specified ticket. Notes by default are private (AKA not visible to non-agents) unless you set the 'private' command argument to False. demisto parameter: (number) ticket_id ID of the ticket you wish to make a note for demisto parameter: (string) body Content of the note in HTML format demisto parameter: (boolean) private Set to false if the note is not private demisto parameter: (number) user_id ID of the agent who is adding the note demisto parameter: (list) notify_emails Array of email addresses of agents/users who need to be notified about this note demisto parameter: (boolean) incoming Set to true if a particular note should appear as being created from outside (i.e., not through web portal). demisto parameter: (list) attachments Entry IDs of files to attach to the note. The total size of these attachments cannot exceed 15MB. returns: Note Object """ # Get command arguments args = demisto.args() # Make request and get raw response note = create_ticket_note(args) # Parse response into context context = {string_to_context_key(key): val for key, val in note.items() if val} context = reformat_conversation_context(context) # Parse attachments into context context, context_readable = attachments_into_context(note, context) context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) complete_context = {"ID": int(note.get("ticket_id")), "Conversation": context} title = "Note for Ticket #{}".format(note.get("ticket_id")) human_readable = tableToMarkdown(title, context_readable, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": note, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": complete_context}, } ) def get_ticket_conversations(ticket_id): endpoint_url = f"tickets/{ticket_id}/conversations" response = http_request("GET", endpoint_url) return response def get_ticket_conversations_command(): """ Lists all replies and notes for a specified ticket. demisto parameter: (number) ticket_id ID of the ticket for which you would like to list all of its conversations returns: Conversation Objects """ # Get id number of ticket as cmd arg for which you want to see all the conversations ticket_id = demisto.args().get("ticket_id") # Make request and get raw response conversations = get_ticket_conversations(ticket_id) # Parse response into context contexts = [] readable_contexts = [] for conversation in conversations: context = {string_to_context_key(key): val for key, val in conversation.items() if val} context = reformat_conversation_context(context) # Parse attachments into context context, context_readable = attachments_into_context(conversation, context) context = reformat_ticket_context(context) context_readable = reformat_ticket_context(context_readable) contexts.append(context) readable_contexts.append(context_readable) complete_context = {"ID": int(ticket_id), "Conversation": contexts} title = f"Conversations of Ticket #{ticket_id}" human_readable = tableToMarkdown(title, readable_contexts, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": conversations, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Ticket(val.ID && val.ID === obj.ID)": complete_context}, } ) """<------ CONTACTS ------>""" def list_contacts(filters): # Alter to match API expected inputs updated_since = filters.get("updated_since", None) if updated_since: del filters["updated_since"] filters["_updated_since"] = updated_since endpoint_url = "contacts" response = http_request("GET", endpoint_url, params=filters) return response def list_contacts_command(): """ List all contacts. Lists all contacts matching the specified filters. If no filters are provided then all unblocked and undeleted contacts will be returned. demisto parameter: (number) mobile mobile number to filter the contacts by demisto parameter: (number) phone phone number to filter contacts by demisto parameter: (string) state The state of contacts by which you want to filter the contacts. Values are 'verified', 'unverified', 'blocked', or 'deleted'. demisto parameter: (datetime) updated_since return contacts that have been updated after the timestamp given as this argument value returns: Contact Objects """ # Get command arguments from user filters = demisto.args() # Make request and get raw response contacts = list_contacts(filters) # Parse response into context contexts = [] for contact in contacts: # Parse individual contact response in context context = format_contact_context(contact) contexts.append(context) filters_as_strings = ", ".join([f"{key}: {val}" for key, val in filters.items()]) title = f"Contacts Filtered by {filters_as_strings}" if filters else "All Contacts" human_readable = tableToMarkdown(title, contexts, removeNull=False) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": contacts, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Contact(val.ID && val.ID === obj.ID)": contexts}, } ) def get_contact(args): contact_id = 0 if not args: err_msg = "You must provide a value for either the mobile, email or id command argument fields." return_error(err_msg) elif args.get("id") is not None: contact_id = args.get("id") elif args.get("email") is not None: try: filters = {"email": args.get("email")} # Get id field of first result of contacts with that email (There should only be one) contact_id = list_contacts(filters)[0].get("id") # If there is an IndexError, it means no results were returned for the given filter except IndexError: err_msg = ( "Couldn't find a contact with that email address." " Double check that you wrote the email address correctly" " and/or that you have a FreshDesk contact with that exact" " email address." ) return_error(err_msg) except Exception as e: return_error(e) else: try: filters = {"mobile": args.get("mobile")} # Get id field of first result of contacts with that mobile number contact_id = list_contacts(filters)[0].get("id") # If there is an IndexError, it means no results were returned for the given filter except IndexError: err_msg = ( "Couldn't find a contact with that mobile number." " Double check that you wrote it correctly and/or that " "you have a FreshDesk contact with that exact mobile number." ) return_error(err_msg) except Exception as e: return_error(e) endpoint_url = f"contacts/{contact_id}" response = http_request("GET", endpoint_url) return response def get_contact_command(): """ View the details of the contact specified by the ID number. demisto parameter: (number) id ID of the contact you wish to view the details of demisto parameter: (number) mobile Mobile number of the contact you wish to view the details of demisto parameter: (string) email Email address of the contact you wish to view the details of returns: Contact Object """ # Get command arguments from user args = demisto.args() # Make request and get raw response contact = get_contact(args) context = format_contact_context(contact) title = "Viewing Contact #{}".format(contact.get("id")) human_readable = tableToMarkdown(title, context, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": contact, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Contact(val.ID && val.ID === obj.ID)": context}, } ) """<------ CANNED RESPONSES ------>""" def list_canned_response_folders(): endpoint_url = "canned_response_folders" response = http_request("GET", endpoint_url) return response def list_canned_response_folders_command(): """ List all Canned Response Folders (Only users with Admin Privileges). returns: Folder Objects """ # Make request and get raw response cr_folders = list_canned_response_folders() # Parse response into context contexts = [] for folder in cr_folders: # Parse individual contact response in context context = {string_to_context_key(key): val for key, val in folder.items() if val} context = reformat_canned_response_context(context) contexts.append(context) title = "All Canned Response Folders" human_readable = tableToMarkdown(title, contexts, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": cr_folders, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.CRFolder(val.ID && val.ID === obj.ID)": contexts}, } ) def get_canned_response_folder(id): endpoint_url = f"canned_response_folders/{id}/responses" response = http_request("GET", endpoint_url) return response def get_canned_response_folder_command(): """ View the details of all the Canned Responses in a Folder. demisto parameter: (number) id ID of the Folder containing the Canned Responses you wish to view the details of returns: Canned Response Objects with more details, aka all of a Canned Response Object's attributes """ # Get id of the containing canned response folder as cmd argument cr_folder_id = demisto.args().get("id") # Make request and get raw response canned_responses = get_canned_response_folder(cr_folder_id) # Parse the responses into context contexts = [] readable_contexts = [] for cr in canned_responses: context = {string_to_context_key(key): val for key, val in cr.items() if val} context = reformat_canned_response_context(context) context, context_readable = attachments_into_context(cr, context) contexts.append(context) readable_contexts.append(context_readable) title = f"Details of Canned Responses in CR Folder #{cr_folder_id}" human_readable = tableToMarkdown(title, readable_contexts, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": canned_responses, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.CRFolder(val.ID && val.ID === obj.ID).CR(val.ID && val.ID === obj.ID)": contexts}, } ) """<------ GROUPS ------>""" def list_groups(): endpoint_url = "groups" response = http_request("GET", endpoint_url) return response def list_groups_command(): """ List all groups. returns: Group Objects """ # Make request and get raw response groups = list_groups() # Parse response into context contexts = [] for group in groups: # Parse individual group response in context context = {} for key, val in list(group.items()): if val: if key == "agent_ids": key = "agent_id" new_key = string_to_context_key(key) if "Id" in new_key: new_key = new_key.replace("Id", "ID") context[new_key] = val contexts.append(context) title = "All Groups" human_readable = tableToMarkdown(title, contexts, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": groups, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Group(val.ID && val.ID === obj.ID)": contexts}, } ) """<------ AGENTS ------>""" def list_agents(filters={}): endpoint_url = "agents" response = http_request("GET", endpoint_url, params=filters) return response def list_agents_command(): """ List agents that match the criteria of the filters entered as command arguments. demisto parameter: (number) mobile Mobile phone number to filter agents by demisto parameter: (number) phone Telephone number to filter agents by demisto parameter: (string) state Filter agents by whether they are 'fulltime' or 'occasional' returns: Agent Objects """ # Get filter as cmd arg args = demisto.args() # Make request and get raw response agents = list_agents(args) # Parse response into context contexts = [] for agent in agents: # Parse the individual agent into context context = {} for key, val in list(agent.items()): if val: if key == "group_ids": key = "group_id" elif key == "role_ids": key = "role_id" new_key = string_to_context_key(key) if "Id" in new_key: new_key = new_key.replace("Id", "ID") context[new_key] = val context["Contact"] = {string_to_context_key(key): val for key, val in agent.get("contact").items() if val} contexts.append(context) title = "All Agents" human_readable = tableToMarkdown(title, contexts, removeNull=True) demisto.results( { "Type": entryTypes["note"], "ContentsFormat": formats["json"], "Contents": agents, "ReadableContentsFormat": formats["markdown"], "HumanReadable": human_readable, "EntryContext": {"Freshdesk.Agent(val.ID && val.ID === obj.ID)": contexts}, } ) """ COMMANDS MANAGER / SWITCH PANEL """ # Commands Switch Panel commands = { "fd-create-ticket": create_ticket_command, "fd-update-ticket": update_ticket_command, "fd-get-ticket": get_ticket_command, "fd-get-contact": get_contact_command, "fd-list-contacts": list_contacts_command, "fd-list-canned-response-folders": list_canned_response_folders_command, "fd-get-canned-response-folder": get_canned_response_folder_command, "fd-list-groups": list_groups_command, "fd-ticket-reply": ticket_reply_command, "fd-create-ticket-note": create_ticket_note_command, "fd-get-ticket-conversations": get_ticket_conversations_command, "fd-list-agents": list_agents_command, "fd-delete-ticket": delete_ticket_command, "fd-search-tickets": search_tickets_command, } LOG(f"Command being called is {demisto.command()}") try: if demisto.command() == "test-module": # This is the call made when pressing the integration test button. test_module() demisto.results("ok") elif demisto.command() == "fetch-incidents": fetch_incidents() elif demisto.command() in commands: # Execute that command commands[demisto.command()]() # Log exceptions except Exception as e: return_error(f"Failed to execute {demisto.command()} command. Error: {str(e)}", e)