CC Customer On All Organisation Requests
We are going to create a trigger to automatically CC a customer on all tickets created for an organisation. The CC address is held in a custom organisation field. You can use one trigger to notify multiple organisations.
The steps to follow are:
- Create a custom organisation field to hold the CC email address
- Create dynamic content to extract any CC user already on the ticket
- Find your API password
- Create an extension to action our CC user
- Create a trigger to pull it all together
This article comes from the original post from Andrey. Within Andrey's article, you will find other options for managing CCs in Zendesk.
Create a custom organisation field
Under Admin>Manage>Organisation Fields, create a new text field.
The naming of the field is important. Here it is called:
- CC User on All Organisation Requests
Zendesk will default the key field value:
- cc_user_on_all_organisation_requests
This will be used to identify the field later.
Create dynamic
Under Admin>Manage>Dynamic Content, we shall create some code to extract any users already CC'ed on a ticket.
Again, naming is important. The dynamic content is named:
- Extract_CC_Mails
The content should hold:
{% if ticket.cc_names != empty %}{% capture ccedusers %}{% for cc in ticket.ccs %}{% unless forloop.last %}{{ cc.email | append: ', ' }}{% else %}{{ cc.email }}{% endunless %}{% endfor %}{% endcapture %}{{ ccedusers | strip_newlines }}{% else %}{% endif %}
Find your API password
Under Admin>Channels>API, enable Token Access.
Add a new token and take note of the long token reference generated. You will use this to authenticate access in your extension.
Create an extension
Extensions are available as trigger actions. This extension will add our organisation's designated user as a CC.
Go to Admin>Settings>Extensions.
Add a new target and select 'URL target' as the type.
There are few settings required here:
Title: This is the name of the extension that will appear in our trigger actions.
Method: PUT
URL: These are the instructions to be processed in our trigger action. Paste the following code:
https://YOURDOMAIN.zendesk.com/api/v2/tickets/{{ticket.id}}.json+?ticket[collaborators]={{dc.extract_cc_mails+|+prepend:','+|+prepend:ticket.organization.custom_fields.cc_user_on_all_organisation_requests}}
Replace the text YOURDOMAIN with the domain of you own help desk.
You will also recognise that our dynamic content name, Extract_CC_Mails, and the key field of our organisation field, cc_user_on_all_organisation_requests
Attribute Name: value
The remaining fields are to authenticate the instructions sent to your Zendesk.
For the username, enter your email address, immediately followed by '/token'.
In the password field, paste the long token reference that you created in the API section.
Create your target or update your target to save any changes.
Create trigger
A trigger will call your extension. Because an organisation field holds the email address of your contact, you only need one trigger to cover all organisations. For example, your conditions may be:
And, the action is to call our extension.
Ensure you include the text in the message box.
You may want to position the trigger near the top of your list to ensure it fires early.
Using the CC field
For each organisation, enter the CC addresses in the custom field. Separate multiple addresses with commas.
When viewing a ticket, click 'Show all events' to see the trigger firing.
-
Nice one Graeme!
I've added a comment to my post referring to this article.
-
This is great, Graeme, and, something a lot of folks are looking for. Thanks for sharing it!
-
Great article, I've got a couple important questions myself and, I'm sure, others utilizing it would find especially clarifying:
- What do you recommend we use now that the single "value" parameter isn't an option? Specifically, the v2 API documentation states all incoming PUT params should be JSON. But clearly the Content type of the API allows for XML and Form input. So what should we use and what do you recommend we pass as the parameters? JSON:{ "ticket[collaborators]": "{{dc.extract_cc_mails_+|+prepend:','+|+prepend:{{ticket.organization.custom_fields.cc_user_on_all_organisation_requests}}"} doesn't seem like it would work.
- There are a couple places that look like they could be typos, but maybe not. Should {{dc.extract_cc_mails_+ be {{dc.extract_cc_mails+ ??? Similarly, you've got {{dc, {{ticket, and }} at the end. So it is really going to correctly parse two {{'s and only one }}, or does that need to be corrected, too?
Thanks!
-
Andrew
Thank you for your feedback. I must have got lucky and the CC still operated correctly. I have updated the text as you suggested as clearly you are correct.
For your first point, on value parameter, unfortunately, I do not know the answer. I am not seeing any change on my account.
-
For those of you that don't have the "value" option, the JSON option seems to be working with me. When you create your trigger, it will ask you what to use for the JSON block, put this:
{"field": "cc_user"}
(Again, it's a dummy value, but it needs to be valid JSON.)
Would love to hear if this is working for someone else consistently. I have it working, but it's not consistent.
-
A few days later and I can report the JSON field works just fine, so for those folks that don't have access to the Value only. JSON with the dummy {"field": "value"} works.
I've also discovered another optimization to the "extract_cc_mail" dynamic content routine that seems to have been copy and pasted quite a bit, if anyone's interested:
I did a little reading up on Liquid and the {% for %} loop automatically checks for empty--in fact, you can use an {% else %} before the end of the {% for %} loop to indicate a state when there are no items to iterate (such as "Nobody"). Given this, you can dramatically simplify your extract_cc_mail routine to:
{% capture ccedusers %}{% for cc in ticket.ccs %}{{ cc.email }}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}{{ ccedusers | strip_newlines }}Hope this helps.
-
Hi Andrew!
Thanks for sharing your solution! :)
-
Spent some more time hacking on this tonight, there's an even more efficient way to do this work. The MAP function in Liquid takes an array and automatically iterates through it and can pull out a specific named part of it (in this case, "email") and then join it together in a string with your given text, in this case ",":
So all of this:
{% capture ccedusers %}{% for cc in ticket.ccs %}{{ cc.email }}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}{{ ccedusers | strip_newlines }}
Becomes this:
{{ ticket.ccs | map: 'email' | join:',' | strip }}
strip not only removes newlines, it also removes leading and trailing spaces.
-
Hi,
Does anyone have any idea how I get the same thing working using a custom field of mine (Drop-down)
https://company.zendesk.com/api/v2/tickets/{{ticket.id}}.json+?ticket[collaborators]={{dc.extract_cc_mails+|+prepend:','+|+prepend:ticket.ticket_field_24561309 == gen_cc}}
Doesn't work with field ID?
This is a custom drop-down field to select a value within, based on the tag gen_cc
ticket.ticket_field_24561309 == gen_cc
Kind regards,
Pierre
-
Wow, Andrew! Look at you go! :) Thanks again for sharing!
-
Nice solution however I need to be able to CC different end users based on ticket content not organisation, any ideas?
Thanks,
Mike.
-
Graeme and Jessie,
I am so appreciative of this initial script. This is the best way for me to learn and get things done for our company. I also believe in giving back to the originator and the community to make us all better. So I'd like to show you what I've found and collect a few of my other comments into one:
1) The Extension and HTTP target has two bugs:
1a) We no longer have access to the "value" option, so we should use "JSON". And it turns out an empty JSON packet works the best, so just: {}. I experimented using {"ticket":{"collaborators":["a@b.com","b@c.com"]}} and due to a limitation in it not expanding "{{dc.extract_cc_mail}}" into individual entries, that approach will not work. So just use a blank packet: {}
1b) You do not need the space between .json and ?, so .json+? should just be .json?
1c) The HTTP target URL is the way to provide multiple collaborators as a comma delimited list with no spaces. But if your CC field is BLANK, the current implementation fails because it will put a stray "," in front of the list, causing the URL call to silently fail. The correction is to move this logic into the extract_cc_mail routine. This optimization also has the benefit of it will be the way Pierre and Mike can both solve their problems, as well. More on that in a follow-up comment. So instead of this:
https://YOURDOMAIN.zendesk.com/api/v2/tickets/{{ticket.id}}.json+?ticket[collaborators]={{dc.extract_cc_mails+|+prepend:','+|+prepend:ticket.organization.custom_fields.cc_user_on_all_organisation_requests}}
Have everyone do this:
https://YOURDOMAIN.zendesk.com/api/v2/tickets/{{ticket.id}}.json?ticket[collaborators]={{dc.extract_cc_mails}}
2) Extract_cc_mails has a number of optimizations I've recommended. You started with this:
{% if ticket.cc_names != empty %}{% capture ccedusers %}{% for cc in ticket.ccs %}{% unless forloop.last %}{{ cc.email | append: ', ' }}{% else %}{{ cc.email }}{% endunless %}{% endfor %}{% endcapture %}{{ ccedusers | strip_newlines }}{% else %}{% endif %}
And then I found that Liquid will automatically skip an empty loop, so you don't need the IF at the beginning. I also then found that Liquid has a STRIP command that will also strip leading and trailing whitespace (needed to compact a multi-user email list--we can't validate that our users won't enter "a@b.com, b@c.com" with a space in between, or maybe even "a@b.com; b@c.com", so we can bulletproof a little more.
There is also MAP and JOIN commands that allow us to pull a field right from a structure and iterate automatically. This dramatically reduces the extract_cc_mails. But now we need to introduce the logic to not only grab the CC field, but also add the CC Org users field. So we end up with a dc.extract_cc_mails routine that looks like this:
{% assign ticket_cc = ticket.ccs | map: 'email' | join:',' | strip %}{% assign org_cc = ticket.organization.custom_fields.cc_user_on_all_organisation_requests | remove:' ' | remove:'<' | remove:'>' | remove:'"' | remove:'?' | replace:';',',' | replace:'+','%2B' | strip %}{{ ticket_cc }}{% if ticket_cc.size > 0 and org_cc.size > 0 %},{% endif %}{{ org_cc }}
What this does is create two variables, one for the ticket's CC list, and one for the Org's CC list. It sanitizes each set of data--the ticket's CC is well sanitized and validated by Zendesk when it's first entered, but the Org CC list could have a lot of inappropriate parts, so we remove all the obvious ones that would cause failure. It then prints out the cleaned up and condensed ticket's CC (if it's not empty), checks to see whether it needs to join them with a ","--prints that, if so, and then prints the cleaned up Org CC.
This has the benefit of putting the logic into code that can be controlled vs the HTTP target. So both Pierre and Mike, for instance, can then add the specific logic (or different field names) to the dynamic content script.
Hope this helps!
-
@Andrew,
Re point 1a i won't call it a bug. Target V1 and Target V2 are two different features. This post is based on idea which i describe in another article where I used Target V1. For Targets V1 you can specify "value" attribute. While for Targets V2 its different. On my test account i can choose which target to use (see link below).
https://www.evernote.com/l/AQOiIR8gApdPhIa1nwFQwn_xMKCPkwrNWc0
-
Hello,
first, thank you guys for sharing a solution to help on a daily recurring task. This would be very valuable for us as our agents are currently adding CCs manually on every ticket on a daily basis. However, I'm having some problems getting the solution to work (testing first in our SANDBOX). Pardon my newbiew question (not familiar with Liquid).
I first added Organization Custom field (cc_user_on_all_organization_requests) as described.
Then set up dynamic content (dc.extract_cc_mails):
{% assign ticket_cc = ticket.ccs | map: 'email' | join:',' | strip %}{% assign org_cc = ticket.organization.custom_fields.cc_user_on_all_organisation_requests | remove:' ' | remove:'<' | remove:'>' | remove:'"' | remove:'?' | replace:';',',' | replace:'+','%2B' | strip %}{{ ticket_cc }}{% if ticket_cc.size > 0 and org_cc.size > 0 %},{% endif %}{{ org_cc }}
After a quick browse of the Liquid syntax documentation this seems logical and should work from what I can tell.
I have set up an HTTP Target extension to use this (with "<MYDOMAIN>" replaced with our actual SANDBOX domain):
URL: https://<MYDOMAIN>.zendesk.com/api/v2/tickets/{{ticket.id}}.json?ticket[collaborators]={{dc.extract_cc_mails}}
Method: PUT
Content type: JSON
Authentication: <email>/token (with valid e-mail address) and API Token for password
..and finally a trigger to run at ticket create to push to the target, with an empty JSON Body:{}
I also tried the following versions for the JSON Body in the trigger with no success:
{"field": "value"}
{"field": "cc_user"}
I can see in a test ticket that the trigger pushes to the target. However, I'm not getting any CCs added in the created ticket. Can anyone point out where I'm going wrong? Help would be greatly appreciated. -
Petri
Tricky isn't it?
To use the new URL Target, Andrew recommends using {} as the JSON content.
I have found that works for me, but only if I use the original URL in the article.
and not the revised URL that you have mentioned.
Using the revised URL, I get the same as you. The extension can be tested successfully, fires on the ticket but no CC is added.
I am that sure Andrew will be better at explaining the process than me. But, that might get you started.
-
Hi Graeme,
thanks for your quick response! I tested that and indeed it works! However, I tested it now on our production Zendesk.
It seems for some reason that didn't (still doesn't!) seem to work on my Zendesk SANDBOX (also, adding CC through API did not seem to work on SANDBOX, which led me to carefully try it out in production).
-
Hi you two! Check to make sure the field name is the same, because there's a little bit of translating we've done between the British english spelling vs. the American english spelling. That is, make sure:
ticket.organization.custom_fields.cc_user_on_all_organisation_requests
Is correctly spelled (vs. what you've used) and:
{{dc.extract_cc_mails}}
Is also the same name.
-
Thanks Andrew!
-
How about some open kimono to help give back to the community! Now, I've taken what Graeme wrote and made it a little more complicated to account for a couple Zendesk bugs or limitations in their Liquid implementation (2.6.2)...
Here's my field name:
cc_users_on_all_organization_requests
Here's my dc (note that I talked to Zendesk support, and they do not support the latest Liquid implementation so "strip" doesn't do anything, you can use "strip_newlines"):
{{dc.extract_cc_emails}}
{% assign ccs = ticket.ccs | map:'email' | join:',' | append:',' | append:ticket.organization.custom_fields.cc_users_on_all_organization_requests | strip_newlines | remove:' ' | remove:'<' | remove:'>' | remove:'"' | remove:'?' | replace:';',',' | downcase | replace:'+','%2B' | prepend:'%start%' | remove:'%start%,' | remove:'%start%' | append:'%end%' | remove:',%end%' | remove:'%end%' | split:',' | sort %}{% assign new_ccs = '' %}{% for cc in ccs %}{% if cc.size > 3 %}{% assign cc_bookends = '%start%' | append:cc | append:'%end%' %}{% unless new_ccs contains cc_bookends %}{% assign new_ccs = new_ccs | append:cc_bookends %}{% endunless %}{% endif %}{% endfor %}{% assign new_ccs = new_ccs | replace:'%end%%start%',',' | remove:'%start%' | remove:'%end%' %}{{ new_ccs }}{% comment %}We do this horrific for loop to check for duplicates because Zendesk only supports Liquid 2.6.2, which doesn't have strip or uniq. So if someone specifies the same email twice in the org_cc, it'll make two copies in the ticket cc.{% endcomment %}
Here's my trigger, which also sends me an email every time it does this so I can see that it's working correctly:
The JSON blanks out collaborators due to the bug of having duplicates:
{"ticket":{"collaborators":[]}}
The Email body (if you want to use it for debugging) is:
dc.extract_cc_emails:
[{{dc.extract_cc_emails}}]
ticket.organization.custom_fields.cc_users_on_all_organization_requests:
[{{ticket.organization.custom_fields.cc_users_on_all_organization_requests}}]And here's the HTTP target:
And the URL from the put:
Yes, there are TWO ticket[collaborators], because there is a bug where it doesn't clear out the previous one before adding the new people and can get duplicates.
One last thing: I've noticed when staring at a ticket and adding a "ccadd" comment that the ticket WILL get updated but the CC field sometimes doesn't show the latest content, so I always manually force-refresh the page in my web browser that is showing a ticket after I've seen the trigger run to make sure the CC field is showing the latest updated information.
Hope this helps!
...A
-
@Andrew,
good catch, thanks! The problem was indeed between British and American english in the DC vs. Organization field name.
-
Hi Petri, glad you got it working!
Andrew, thanks for your help for for posting your solution! If you'd like to post your modified solution as a separate new tip for users (with a link back to Graeme's original) we'll send you swag. :)
Thanks for being part of the community!
-
Hi Jennifer! I'd be happy to, but I'm really just advancing Graeme and Andrey's work, so it seemed appropriate to post in their articles. If you all feel otherwise, just ping me at my email and we can discuss!
-
Hi all,Great tip for adding CC !Does someone has a great tip to notify by email these new CCed users when ticket is created ? (and only these new ones)Initial CCed users will automatically received an email notification when ticket is created, it's seem to be by design. But these custom added CCed users will not, because of asynchronous API call.
-
@Yannick,
Actually, the "Email users on new ticket" looks to be a trigger, so as long as you have this trigger run AHEAD of that, you will, in fact, get these people emailed as part of the initial creation alert. Have you seen the upper right "edit" button to the triggers page that allows you to drag their order around?
Cheers,
...A
-
Hi Andrew,
Here is my ticket events :
1/ First event : new ticket including a existing CC user
We see notifying CC is not a trigger, it's integrated in my Zendesk.
2/ Second event, result of URL target's trigger (add a second CC user) :
even if the URL target trigger is set at the top, it's an asynchronous process. So CC email ("YL public") added by web service is not present at the time of the first event. It comes as a second event, after CC notification occured in the first event.So...Is it possible we have not the same Zendesk platform ? or I missing something...
-
This is brilliant!
Thanks for sharing!
-
I've tried variations with both target URL and target HTTP and though I can prove that my constructed CC list is be built correct, I'm never actually seeing the emails added to the CC list of the ticket. :-( Do the emails have to be registered with existing user accounts? I was hoping to use this technique as a way to get around that silly limitation in Zendesk, and considering the purpose of this post, I assumed this made it possible to do so. The emails I'm trying to add through the target HTTP do not exist as users in our ZD instance, so I'm wonder if that's why I'm never seeing the emails added to the CC list.
-
@Yannick: I think you're on to something. Because I've noticed similar when I've wanted to add a Cc'ed user and have them see a comment I'm adding, I can't do them at the same time. I have to add the Cc'ed user, submit as open, then add the comment, then submit as open. So you must be right that there's a non-deterministic ordering going on.
@Daniel: you definitely do NOT need these people to be valid Zendesk accounts, as you say, it's a way around that whole problem. It sounds to me like maybe your HTTP or URL target might be misconfigured--did you actually do the test option and see a reasonable response? Because when you test it you should get the resulting record back of a test record. Here's another thing you can do, too: add an "email" event that emails just you every time the trigger runs and have it send you the {{dc.extract_mail_ccs}} in the body of the email and you'll be able to see if that's resulting in anything meaningful.
-
This is amazing Graeme,
I have 1 question. I got the trigger & the target working, but the notification email is not sent to the CC-ed person added by the trigger after the ticket creation. However, the email is sent after the first reply. Meaning that the newly added CC is not notified with after the ticket creation.
Any ideas, how can I overcome this problem?
Thanks,
-
Hossam
Sorry to say that the newly added CC not being notified on the ticket creation is a limitation of this approach.
You may want to explore the user settings. If you give a user access 'to view tickets from 'user's org', the user will then have the option to 'follow' all organisation requests. The user can either set this up, or you can assume their identity and set it up for them.
Iniciar sesión para dejar un comentario.
44 Comentarios