Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
api-webhooks [2016/01/19 14:06]
ben created
api-webhooks [2017/07/26 08:24] (current)
Line 1: Line 1:
-===== Contact Unsubscribe Webhook ​=====+===== Sentori Webhooks ​=====
  
-When a Contact unsubscribes,​ you can have Sentori pass this information on to an external system, such as your CRM system, to keep everything in sync.+When a Contact ​[[api-webhooks#​contact-subscribe-webhook|signs up]], [[api-webhooks#​contact-unsubscribe-webhook|unsubscribes]], [[api-webhooks#​email-bounce-webhook|has a bounce logged against them]] or [[api-webhooks#​interactions-webhook|performs an interaction with one of your Emails]], you can have Sentori pass this information on to an external system, such as your CRM system, to keep everything in sync.
  
-To do this, you must setup the Contact ​Unsubscribe ​Webhook, found by going to Settings, then API.+===== Contact Subscribe Webhook ===== 
 + 
 +Sentori calls this Webhook when a Contact signs up by clicking the Opt In Confirmation Link after submitting a Sentori Form, or when a User manually adds a single Contact. 
 + 
 +To use this featureset the Contact ​Subscribe ​Webhook ​for your account ​by signing in to Sentori then going to the Settings ​menu and choosing ​API
 + 
 +Note: This Webhook is //not// called when uploading Contacts.
  
 ==== Request ==== ==== Request ====
  
-This is how Sentori structures its request to your external system's webhook ​when a Contact ​unsubscribes:+This is how Sentori structures its request to your external system when a Contact ​subscribes:
  
-|HTTP Method|POST|+===HTTP MethodPOST===
  
 +===HEADERS===
 +|Content-Type|application/​json|
 +
 +===BODY===
 +|AccountExternalID|The External ID of the Account the Contact has subscribed to.|
 +|EmailAddress|The email address of the Contact that has subscribed.|
 +|Type|The string "​subscribe"​.|
 +|Date|The date and time when the sign up occurred. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​|
 +|Source|Indicates how the Contact was subscribed. ​ If they signed up through a Sentori Form this will be the URL where the Form was hosted. ​ If a Sentori User created the Contact this will be the word "​User"​.|
 +|vhash|A hashed value used to confirm it's a genuine notification from Sentori.|
 +|Description|A human-readable message containing the other values.|
 +
 +==== Example Request from Sentori ====
 +Note the Source value of "​User",​ indicating the Contact was created by a User within Sentori.
 +<​code>​
 +POST [your webhook url] HTTP/1.1
 +Content-Type:​ application/​json
 +User-Agent: Sentori API
 +Content-Length:​ 314
 +
 +{
 +  "​AccountExternalID"​ : "​1234ABCD",​
 +  "​EmailAddress"​ : "​user@example.com",​
 +  "​Type"​ : "​subscribe",​
 +  "​Date"​ : "​2016-10-28T14:​59:​43.6889402Z",​
 +  "​Source"​ : "​User",​
 +  "​vhash"​ : "​EC0A73A0091CF860DCBD0B572CD8042A9B702D7B",​
 +  "​Description"​ : "​\"​user@example.com\"​ logged \"​subscribe\"​ Event at \"Fri 28 Oct 2016 14:59 UTC\"​."​
 +}
 +</​code>​
 +
 +==== Confirm the Request is Genuine ====
 +To confirm the request isn't from another system impersonating Sentori, perform the following operation.
 +
 +  - Concatenate the //​AccountExternalID//,​ //​EmailAddress//,​ //Type//, //Date//, //Source// values from the request (so exclude //​Description//​ and //vhash//) and your //API Key//
 +  - Convert the result into bytes
 +  - Get the SHA1 hash of those bytes and remove any hyphen characters
 +
 +If the result matches the //vhash// value in the request, it's genuine.
 +
 +\\
 +\\
 +
 +Here's the example above being checked using C# code:
 +
 +The API Key of this Sentori Account is "​20011111-1111-1111-1111-111111111200"​.
 +
 +<​code>​
 +string values = "​1234ABCD"​ + "​user@example.com"​ + "​subscribe"​ + "​2016-10-28T14:​59:​43.6889402Z"​ + "​User"​ + "​20011111-1111-1111-1111-111111111200";​
 +byte[] bytes = System.Text.Encoding.Default.GetBytes(values);​
 +System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed();​
 +string checkHash = BitConverter.ToString(sha1.ComputeHash(bytes));​
 +checkHash = checkHash.Replace("​-",​ string.Empty);​
 +// Output is "​true"​ if genuine, "​false"​ if not.
 +Console.WriteLine("​Is genuine? " + ("​EC0A73A0091CF860DCBD0B572CD8042A9B702D7B"​ == checkHash));​
 +</​code>​
 +
 +\\
 +\\
 +
 +===== Contact Unsubscribe Webhook =====
 +
 +Sentori calls this Webhook when a Contact becomes unsubscribed,​ either by the Unsubscribe link in an Email being clicked or a User manually unsubscribing them in Sentori.
 +
 +To use this feature, set the Contact Unsubscribe Webhook for your account by signing in to Sentori then going to the Settings menu and choosing API.
 +
 +Note: This Webhook is //not// called when uploading a Suppression List.
 +==== Request ====
 +
 +This is how Sentori structures its request to your external system when a Contact unsubscribes:​
 +
 +===HTTP Method: POST===
  
-HEADERS+===HEADERS===
 |Content-Type|application/​json| |Content-Type|application/​json|
  
-BODY+===BODY===
 |AccountExternalID|The External ID of the Account the Contact has unsubscribed from.| |AccountExternalID|The External ID of the Account the Contact has unsubscribed from.|
 |EmailAddress|The email address of the Contact that has unsubscribed.| |EmailAddress|The email address of the Contact that has unsubscribed.|
 |Type|The string "​unsubscribe"​.| |Type|The string "​unsubscribe"​.|
 +|Date|The date and time when the unsubscribe occurred. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​|
 |vhash|A hashed value used to confirm it's a genuine notification from Sentori.| |vhash|A hashed value used to confirm it's a genuine notification from Sentori.|
 +|Description|A human-readable message containing the other values.|
  
 +==== Example Request from Sentori ====
 +<​code>​
 +POST [your webhook url] HTTP/1.1
 +Content-Type:​ application/​json
 +User-Agent: Sentori API
 +Content-Length:​ 314
  
-To confirm the notification is genuinenot from another system impersonating Sentoriperform the following operation If the result matches the "​vhash" ​valueit's genuine.+
 +    "​AccountExternalID":"​1234ABCD"​, 
 +    "​EmailAddress":"​user@example.com"​, 
 +    "​Type":"​unsubscribe",​ 
 +    "​Date":"​2016-02-01T14:​12:​59.1230000Z",​ 
 +    ​"​vhash"​:"​F88EA6387F4CF9F258521444C34FEB526DF66A55"​, 
 +    "​Description":"​\"​user@example.com\" logged \"​unsubscribe\"​ Event at \"Mon 01 Feb 2016 14:12 UTC\"​."​ 
 +
 +</​code>​
  
-SHA1Hashed( GetBytes( AccountExternalID + EmailAddress + Type + API Key ) ).Replace("​-"​""​)+==== Confirm the Request is Genuine ==== 
 +To confirm the request isn't from another system impersonating Sentoriperform the following operation.
  
 +  - Concatenate the //​AccountExternalID//,​ //​EmailAddress//,​ //Type//, //Date// values from the request (so exclude //​Description//​ and //vhash//) and your //API Key//
 +  - Convert the result into bytes
 +  - Get the SHA1 hash of those bytes and remove any hyphen characters
  
 +If the result matches the //vhash// value in the request, it's genuine.
 +
 +\\
 +\\
 +
 +Here's the example above being checked using C# code:
 +
 +The API Key of this Sentori Account is "​20011111-1111-1111-1111-111111111200"​.
 +
 +<​code>​
 +string values = "​1234ABCD"​ + "​user@example.com"​ + "​unsubscribe"​ + "​2016-02-01T14:​12:​59.1230000Z"​ + "​20011111-1111-1111-1111-111111111200";​
 +byte[] bytes = System.Text.Encoding.Default.GetBytes(values);​
 +System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed();​
 +
 +string checkHash = BitConverter.ToString(sha1.ComputeHash(bytes));​
 +checkHash = checkHash.Replace("​-",​ string.Empty);​
 +
 +// Output is "​true"​ if genuine, "​false"​ if not.
 +Console.WriteLine("​Is genuine? " + ("​F88EA6387F4CF9F258521444C34FEB526DF66A55"​ == checkHash));​
 +</​code>​
 +
 +\\
 +\\
 +
 +===== Mailing List Subscribe Webhook =====
 +
 +Sentori calls this Webhook when a Contact is added to a Mailing List, whether by a Contact performing an action or a User through Sentori'​s interface.
 +
 +Note: This Webhook is //not// called when uploading Contacts.
  
 ==== Request ==== ==== Request ====
  
-POST [webhook url]+This is how Sentori structures its request to your external system when a Contact is added to a Mailing List:
  
-HEADERS+===HTTP Method: POST=== 
 + 
 +===HEADERS=== 
 +|Content-Type|application/​json| 
 + 
 +===BODY=== 
 +|AccountExternalID|The External ID of the Account this relates to.| 
 +|EmailAddress|The email address of the Contact that has subscribed.| 
 +|MailingListExternalID|The External ID of the Mailing List subscribed to.| 
 +|Type|The string "​subscribe"​.| 
 +|Date|The date and time when the operation occurred. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​| 
 +|vhash|A hashed value used to confirm it's a genuine notification from Sentori.| 
 +|Description|A human-readable message containing the other values.| 
 + 
 +==== Example Request from Sentori ==== 
 +<​code>​ 
 +POST [your webhook url] HTTP/1.1
 Content-Type:​ application/​json Content-Type:​ application/​json
 +User-Agent: Sentori API
 +Content-Length:​ 410
  
-BODY 
 { {
- "​AccountExternalID":"​1234ABCD",​ +  ​"​AccountExternalID"​ : "​1234ABCD",​ 
- "​EmailAddress":"​user@example.com",​ +  "​EmailAddress"​ : "​user@example.com",​ 
- "​Type":"​unsubscribe", +  "​MailingListExternalID"​ : "​ABCD1234",​ 
- "​vhash":"​49D0648E0A7A1C25E94405575B03BA3625A969589"+  ​"​Type"​ : "subscribe", 
 +  "​Date"​ : "​2016-10-28T14:​59:​43.6889402Z",​ 
 +  ​"​vhash"​ : "EC0A73A0091CF860DCBD0B572CD8042A9B702D7B",​ 
 +  "​Description"​ : "​\"​user@example.com\"​ logged \"​subscribe\"​ Event for Mailing List \"​Members Offers\"​ (ExternalID:​ ABCD1234) at \"Fri 28 Oct 2016 14:59 UTC\"."
 } }
 +</​code>​
  
-|| <nowiki>https://api.sentoriapp.com/v2.0/contact/add/joe%40example.sentoriapp.com/</nowiki>| + 
-|MethodPOST|+==== Confirm the Request is Genuine ==== 
 +To confirm the request isn't from another system impersonating Sentori, perform the following operation. 
 + 
 +  - Concatenate the //​AccountExternalID//,​ //​EmailAddress//,​ //​MailingListExternalID//,​ //Type//, //Date// values from the request (so exclude //​Description//​ and //vhash//) and your //API Key// 
 +  - Convert the result into bytes 
 +  - Get the SHA1 hash of those bytes and remove any hyphen characters 
 + 
 +If the result matches the //vhash// value in the request, it's genuine. 
 + 
 +\\ 
 +\\ 
 + 
 +Here's the example above being checked using C# code: 
 + 
 +The API Key of this Sentori Account is "​20011111-1111-1111-1111-111111111200"​. 
 + 
 +<​code>​ 
 +string values = "​1234ABCD"​ + "​user@example.com"​ + "​ABCD1234"​ + "​subscribe"​ + "​2016-10-28T14:​59:​43.6889402Z"​ + "​20011111-1111-1111-1111-111111111200";​ 
 +byte[] bytes = System.Text.Encoding.Default.GetBytes(values);​ 
 +System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed();​ 
 +string checkHash = BitConverter.ToString(sha1.ComputeHash(bytes));​ 
 +checkHash = checkHash.Replace("​-",​ string.Empty);​ 
 +// Output is "​true"​ if genuine, "​false"​ if not. 
 +Console.WriteLine("​Is genuine? " + ("​0321B442144F3EF8663EF47D738E2EDEDEBDF73F"​ == checkHash));​ 
 +</​code>​ 
 + 
 +\\ 
 +\\ 
 + 
 + 
 +===== Mailing List Unsubscribe Webhook ===== 
 + 
 +Sentori calls this Webhook when a Contact is removed from a Mailing List, whether by a Contact performing an action (including Unsubscribing from the Account) or a User through Sentori'​s interface. 
 + 
 +Note: This Webhook is //not// called when uploading a Suppression List. 
 + 
 +==== Request ==== 
 + 
 +This is how Sentori structures its request to your external system when a Contact is removed from a Mailing List: 
 + 
 +===HTTP Method: POST=== 
 + 
 +===HEADERS=== 
 +|Content-Type|application/​json| 
 + 
 +===BODY=== 
 +|AccountExternalID|The External ID of the Account this relates to.| 
 +|EmailAddress|The email address of the Contact that has unsubscribed.| 
 +|MailingListExternalID|The External ID of the Mailing List unsubscribed from.| 
 +|Type|The string "​unsubscribe"​.| 
 +|Date|The date and time when the operation occurred. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​| 
 +|vhash|A hashed value used to confirm it's a genuine notification from Sentori.| 
 +|Description|A human-readable message containing the other values.| 
 + 
 +==== Example Request from Sentori ==== 
 +<code> 
 +POST [your webhook url] HTTP/1.1 
 +Content-Typeapplication/json 
 +User-Agent: Sentori API 
 +Content-Length:​ 414 
 + 
 +
 +  "​AccountExternalID"​ : "​1234ABCD",​ 
 +  "​EmailAddress"​ : "​user@example.com",​ 
 +  "​MailingListExternalID"​ : "​ABCD1234",​ 
 +  "​Type"​ : "​unsubscribe",​ 
 +  "​Date"​ : "​2016-10-28T14:​59:​43.6889402Z",​ 
 +  "​vhash"​ : "​EC0A73A0091CF860DCBD0B572CD8042A9B702D7B",​ 
 +  "​Description"​ : "​\"​user@example.com\"​ logged \"​unsubscribe\"​ Event for Mailing List \"​Members Offers\"​ (ExternalID:​ ABCD1234) at \"Fri 28 Oct 2016 14:59 UTC\"​."​ 
 +
 +</code> 
 + 
 +==== Confirm the Request is Genuine ==== 
 +To confirm the request isn't from another system impersonating Sentori, perform the following operation. 
 + 
 +  - Concatenate the //​AccountExternalID//,​ //​EmailAddress//,​ //​MailingListExternalID//,​ //Type//, //Date// values from the request (so exclude //​Description//​ and //vhash//) and your //API Key// 
 +  - Convert the result into bytes 
 +  - Get the SHA1 hash of those bytes and remove any hyphen characters 
 + 
 +If the result matches the //vhash// value in the request, it's genuine. 
 + 
 +\\ 
 +\\ 
 + 
 +Here's the example above being checked using C# code: 
 + 
 +The API Key of this Sentori Account is "​20011111-1111-1111-1111-111111111200"​. 
 + 
 +<​code>​ 
 +string values = "​1234ABCD"​ + "​user@example.com" + "​ABCD1234"​ + "​unsubscribe"​ + "​2016-10-28T14:​59:​43.6889402Z"​ + "​20011111-1111-1111-1111-111111111200";​ 
 +byte[] bytes = System.Text.Encoding.Default.GetBytes(values);​ 
 +System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed();​ 
 +string checkHash = BitConverter.ToString(sha1.ComputeHash(bytes));​ 
 +checkHash = checkHash.Replace("​-",​ string.Empty);​ 
 +/Output is "​true"​ if genuine, "​false"​ if not. 
 +Console.WriteLine("​Is genuine? " + ("​6769BC487AB5F54FE2AA5858FB6260FBFB0DD7A2"​ == checkHash));​ 
 +</code> 
 + 
 +\\ 
 +\\ 
 + 
 + 
 +===== Email Bounce Webhook ===== 
 + 
 +Sentori calls this Webhook when a bounce occurs as a result of attempting to send an email to a Contact. 
 + 
 +To use this feature, set the Email Bounce Webhook for your account by signing in to Sentori then going to the Settings menu and choosing API. 
 + 
 +Depending on the information contained in a bounce and whether any previous bounces have occurred, Sentori will either log the bounce and immediately set the Contact to "​Bad"​ (by setting their "Bad Email Address"​ Field to "​Yes"​) or just log the bounce which goes towards a Contact'​s overall bounce count. 
 + 
 +Contacts are marked as "​Bad"​ when their "Bad Email Address"​ boolean Field is set to "​True"​ (or "​Yes"​). ​ Sentori excludes those Contacts from sends. 
 + 
 +==== Request ==== 
 + 
 +This is how Sentori structures its request to your external system when a bounce occurs: 
 + 
 +===HTTP Method: POST=== 
 + 
 +===HEADERS=== 
 +|Content-Type|application/json| 
 + 
 +===BODY=== 
 +|AccountExternalID|The External ID of the Account the bounce relates to.| 
 +|EmailAddress|The email address of the Contact the bounce relates to.| 
 +|ContactMarkedBad|A true or false value indicating whether this bounce resulted in the Contact being marked as "​Bad"​ or not.| 
 +|BounceType|Whether the bounce was Hard (a permanent delivery problem), Soft (a temporary delivery problem), or Spam (the receiving mail server didn't accept it as it appeared to be spam). ​ Hard bounces immediately mark a Contact as "​Bad"​. ​ Soft and Spam bounces ​add to a Contact'​s overall Bounce count and when Sentori has received enough of these will result in them being marked as "​Bad"​.| 
 +|BounceReason|Text briefly describing the problem indicated by the received bounce.| 
 +|Date|The date and time when this bounce message was received by Sentori. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​| 
 +|vhash|A hashed value used to confirm it's a genuine notification from Sentori.| 
 +|Description|A human-readable message containing the other values. ​ This will change slightly to reflect whether ContactMarkedBad is true or false.| 
 + 
 +==== Example Request from Sentori ==== 
 +<​code>​ 
 +POST [your webhook url] HTTP/1.
 +Content-Type:​ application/​json 
 +User-Agent: Sentori API 
 +Content-Length:​ 455 
 + 
 +
 +    "​AccountExternalID":"​1234ABCD",​ 
 +    "​EmailAddress":"​user@example.com", 
 +    "​ContactMarkedBad":​true,​ 
 +    "​BounceType":"​Hard",​ 
 +    "​BounceReason":"​Email address does not exist",​ 
 +    "​Date":"​2016-03-01T15:​51:​00.1230000Z",​ 
 +    "​vhash":"​1485836BE3A0908DC50A42CC22BB76340EF263C0",​ 
 +    "​Description":"​Bounce logged for email address \"​user@example.com\"​ at \"Tue 01 Mar 2016 15:51 UTC\"​.\\n\\nThe Contact HAS been marked as having a Bad Email Address due to the total number of Bounces logged.\\n\\nBounce Details:​\\nHard\\nEmail address does not exist"​ 
 +
 +</code> 
 + 
 +==== Confirm the Request is Genuine ==== 
 +To confirm the request isn't from another system impersonating Sentori, perform the following operation. 
 + 
 +  - Concatenate the //​AccountExternalID//,​ //​EmailAddress//,​ //​ContactMarkedBad//​ (converting it to a lowercase string, e.g. "​true"​ or "​false"​),​ //​BounceType//,​ //​BounceReason//,​ //Date// values from the request (so exclude //vhash// and //​Description//​) and your //API Key// 
 +  - Convert the result into bytes 
 +  - Get the SHA1 hash of those bytes and remove any hyphen characters 
 + 
 +If the result matches the //vhash// value in the request, it's genuine. 
 + 
 +\\ 
 +\\ 
 + 
 +Here's the example above being checked using C# code: 
 + 
 +The API Key of this Sentori Account is "​20011111-1111-1111-1111-111111111200"​. 
 + 
 +<​code>​ 
 +string values = "​1234ABCD"​ + "​user@example.com"​ + true.ToString().ToLower() + "​Hard"​ + "Email address does not exist" + "​2016-03-01T15:​51:​00.1230000Z"​ + "​20011111-1111-1111-1111-111111111200";​ 
 +byte[] bytes = System.Text.Encoding.Default.GetBytes(values);​ 
 +System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed();​ 
 + 
 +string checkHash = BitConverter.ToString(sha1.ComputeHash(bytes));​ 
 +checkHash = checkHash.Replace("​-",​ string.Empty);​ 
 + 
 +// Output is "​true"​ if genuine, "​false"​ if not. 
 +Console.WriteLine("​Is genuine? " + ("​1485836BE3A0908DC50A42CC22BB76340EF263C0"​ == checkHash));​ 
 +</code> 
 + 
 + 
 + 
 + 
 +\\ 
 +\\ 
 + 
 +===== Interactions Webhook ===== 
 + 
 +Sentori calls this Webhook when a Contact first performs certain interactions with an Email (e.g. opens the email or clicks a link) you can have Sentori pass this information on to an external system that may perform additional tasks. 
 + 
 +At present, this is limited to External Link Clicks only. 
 + 
 +NOTE: Sentori will only call your webhook URL the first time each of your Contacts performs one of your selected interactions,​ not every time.  So (once we've implemented this webhook for Opens) if a Contact Opens your email 5 times, it would only be called for the first Open.  Each link is considered a separate interaction so it would be called the first time a Contact clicks each link in an Email. 
 + 
 + 
 +==== Request ==== 
 + 
 +This is how Sentori structures its request to your external system when a selected interaction occurs: 
 + 
 +===HTTP Method: POST=== 
 + 
 +===HEADERS=== 
 +|Content-Type|application/​json| 
 + 
 +===BODY=== 
 +|AccountExternalID|The External ID of the Account the interaction relates to.| 
 +|AccountName|The name of that Sentori Account.| 
 +|EmailExternalID|The External ID of the Email the interaction relates to.| 
 +|EmailName|The name of that Email.| 
 +|Type|The type of interaction,​ e.g. ExternalLinkClick.| 
 +|Value|A value giving more information about the interaction. ​ For an ExternalLinkClick,​ this is the URL of the link.| 
 +|Name|The name given to the item interacted with.  For an ExternalLinkClick,​ this is the Name/Title of the link.| 
 +|ContactSystemID|The SystemID of the Contact that performed the interaction.| 
 +|ContactEmailAddress|The EmailAddress of the Contact that performted the interaction.| 
 +|Date|The date and time when the interaction occurred. ​ Formatted to ISO 8601, e.g. "​2016-02-01T14:​12:​59.1230000Z"​.| 
 + 
 + 
 +==== Example Request from Sentori ==== 
 +<​code>​ 
 +POST [your webhook url] HTTP/1.1 
 +Content-Type:​ application/​json 
 +User-Agent: Sentori API 
 +Content-Length:​ 376 
 + 
 +
 +  "​AccountExternalID"​ : "​1234ABCD",​ 
 +  "​AccountName"​ : "My Account",​ 
 +  "​EmailExternalID"​ : "​S8JE72AD",​ 
 +  "​EmailName"​ : "​Newsletter",​ 
 +  "​Type"​ : "​ExternalLinkClick",​ 
 +  "​Value"​ : "​http://​website.co.uk/​homepage/#​info",​ 
 +  "​Name"​ : "​Website Homepage",​ 
 +  "​ContactSystemID"​ : 5841, 
 +  "​ContactEmailAddress"​ : "​user@example.com",​ 
 +  "​Date"​ : "​2016-08-04T09:​15:​28.1744295Z"​ 
 +
 +</​code>​