Webhooks enable you to be notified when certain events occur in Smartling. For example, you can configure a webhook to notify you when the translations are ready for download. The notification is via by Smartling via an HTTP request, known as a “callback”, sent to the URL you specify in the webhook configuration.
This sequence is illustrated below:
Callbacks can be used to trigger automated actions in your integration, such as the download of completed translations.
Handling callbacks
To avail of callbacks you must make an internet-accessible web server available to receive and handle them. When your callback-handling server receives a valid callback request, it should respond with an HTTP status code in the 2xx
range to indicate that the callback was successfully received. The Smartling callback service will resend the callback up to 10 times if such a response is not received.
Below are some additional recommendations on handling callbacks:
- Consider restricting access to your callback-handling server to the IP addresses of the Smartling callback service (contact Smartling Support for the list of IP addresses).
- Keep the callback handler small in order to respond quickly to the callback request. In other words, avoid putting business logic inside the request handler.
- Use HTTPS for improved security.
- Consider using signed requests (see below) for authenticating callback requests.
- Don’t rely on callbacks as the only mechanism to know the status of your translations. Instead supplement them with appropriate status polling.
Testing callback
A number of websites, such as https://webhook.site and https://requestbin.com, provide free URLs that can be used to test callbacks. Simply create a URL on one of these sites, then configure your webhook to use it. These sites allow you to see the details of the request sent to them by the callback service.
In addition, the Ngrok service (https://ngrok.com/) allows you to tunnel callback requests to a non-publicly accessible server, enabling you to test your callback-handling code before making it public.
Callback request structure
Depending on the type of webhook, either GET
or POST
callback requests may be sent. Below is an example of a GET
callback request URL associated with a Files API webhook:
https://webhook.site/1e2d7a5d-94d5-4919-847e-b7bea01693bd?locale=fr-FR&publishStatus=published&fileUri=strings-1-5.txt&ts=1620744030201
Note that the Smartling project ID is not included in this URL. If you need additional information such as the project ID, you can include it as part of the callback URL when configuring the webhook.
Below is an example of a POST
callback request body from a Jobs API webhook:
{ "type": "job.completed", "translationJobUid": "es3yo3lb8ykj", "ts": "1644919715512" }
Below is an example of a POST
callback request body from a Strings API webhook:
{ "projectId": "7d964bd0d", "hashcode": "7467e4ace11b903446003bb5a7c10e4a", "localeId": "fr-FR", "type": "string.localeCompleted", "publishStatus": "published", "translations": [ { "translation": "Un exemple", "pluralForm": null, "modifiedDate": "2021-05-11T15:20:06Z" } ], "ts": "1620746412599" }
Additional examples are included below.
You can use the testing websites mentioned above to examine the details of different types of callback requests.
Signed callback requests
Signed callbacks provide greater assurance that incoming callback requests were sent by Smartling and were not tampered with. The process relies on a shared secret held by both the Smartling callback service and your callback handler: Smartling uses the shared secret to generate a signature of the request and includes the signature in a request header; when your callback handler receives the request, it follows the same process using its copy of the shared secret to validate that the result is the same. The process is described in more detail below.
Signing Procedure
Below is the signing procedure for GET requests:
- Generate a timestamp (in milliseconds since January 1st, 1970 at UTC), and add it as an additional parameter
ts
in the request URL - Generate a raw binary HMAC-SHA1 signature of the full request URL using the shared secret
- Base64-encode the resulting signature and add it to a request header named
X-Smartling-Signature
.
Below is the signing procedure for POST requests:
- Generate a timestamp (in milliseconds since January 1st, 1970 at UTC), and add it as an additional node ts in the request body JSON
- ‘Flatten’ the JSON structure. For example, for strings-api
POST
callbacks, this involves moving the contents of thetranslations
node to the top level, changing the key names into the formtranslations[0].key
. See example below. - Sort the JSON content alphabetically by key
- Generate a string from the JSON structure in the form
key1=val1|key2=val2|key3=val3
- Generate a raw binary HMAC-SHA1 signature for this string using the shared secret
- Base64-encode the resulting signature and add it to a request header named
X-Smartling-Signature
.
As an example of steps 2, 3, and 4 in the above process, the following POST body:
{ "projectId": "7d964bd0d", "hashcode": "7467e4ace11b903446003bb5a7c10e4a", "localeId": "fr-FR", "type": "string.localeCompleted", "publishStatus": "published", "translations": [ { "translation": "Un exemple", "pluralForm": null, "modifiedDate": "2021-05-11T15:20:06Z" } ], "ts": "1620746412599" }
is normalized into this string:
hashcode=7467e4ace11b903446003bb5a7c10e4a|localeId=fr-FR|projectId=7d964bd0d|publishStatus=published|translations[0].modifiedDate=2021-05-11T15:20:06Z|translations[0].pluralForm=null|translations[0].translation=Un exemple|ts=1620746412599|type=string.localeCompleted
which is then signed using the shared secret.
Note that the webhook testing websites mentioned above sometimes display request parameters in a different order than they were received. This can result in the wrong signature being generated when testing.
Enabling signed requests
Contact your Smartling CSM to enable signed callbacks for your account. You will need to provide a shared secret in the form of a random string of ASCII characters (ideally with length > 12).
Configuring webhooks
Webhooks are configured via API call. For example, when you upload a file for translation, you can pass the URL to be used for the callback sent when the file is translated. The available webhooks, along with the methods of configuration, are listed below:
Files
Callback event | Description | Where configured |
File published |
Invoked when all the authorized content in a file reaches the Published workflow step in a locale. Separate callbacks are sent for each locale. Method: GET |
The callbackUrl parameter of the Upload file endpoint, the Add file to batch endpoint, or the Upload file to a batch endpoint. |
File prepublished |
Invoked when all the authorized content in a file reaches the Published workflow step, or is prepublished, in a particular locale. Separate callbacks are sent per locale. For details on prepublishing, see Prepublish Translations. This callback is not available by default—it must be enabled by your CSM. Method: GET |
Uses the same configuration as the File published event above. |
File upload completed |
Invoked when the upload of the file completes. This callback is disabled by default. To enable it for your project, please contact your Customer Success Manager. Method: GET |
Uses the same configuration as the File published event above. |
Jobs
Callback event | Description | Where configured |
Job completed |
Invoked when all the authorized content in a job, for all locales, reaches the Published step of the workflow. Method: GET or POST |
The callbackUrl and callbackMethod parameters of the Create job endpoint. |
Job cancelled |
Invoked when a job is cancelled. Method: GET or POST |
Uses the same configuration as the Job completed event above. |
Strings
Callback event | Description | Where configured |
String published |
Invoked when a string translation reaches the Published workflow step in a locale. Separate callbacks are sent per locale. Method: GET or POST |
The callbackUrl and callbackMethod parameters of each string being added via the Add strings endpoint. |
String prepublished |
Invoked when a string translation reaches the Published workflow step, or is prepublished, in a locale. Separate callbacks are sent per locale. For details on prepublishing, see Prepublish Translations. This callback is not available by default—it must be enabled by your CSM. Method: GET or POST |
Uses the same configuration as String published above. |
Issues
Callback event | Description | Where configured |
Various issue-related events. |
Invoked when certain issue-related events occur. Details of the supported events, and the structure of the callback requests can be found at Receive issue webhooks For more information on issues, see Using issues. Method: POST |
Issue webhooks can be enabled via Set up issue webhooks and disabled via Delete issue webhook settings |
Sample callbacks
Below are some examples of callback request GET query strings and POST bodies. You can use the webhook testing websites mentioned above to see the exact requests sent for your integration.
File published (GET)
?locale=ru-RU&publishStatus=published&fileUri=example.properties&ts=1542138000086
File prepublished (GET)
?locale=ru-RU&publishStatus=prepublished&fileUri=example.properties&ts=1542138300048
Job completed (GET)
https://callback.handler.com?type=job.completed&translationJobUid=xm5rj2kmxtaj&ts=1644920029445
Job completed (POST)
{ "type": "job.completed", "translationJobUid": "es3yo3lb8ykj", "ts": "1644919715512" }
Job cancelled (GET)
?type=job.cancelled&translationJobUid=vacryeniyvlk
String published (POST)
{ "projectId": "cef4c4151", "hashcode": "493c3a41b5e4648e26f6bbfb94d12341", "localeId": "fr-FR", "type": "string.localeCompleted", "publishStatus":"published", "translations": [ { "translation": "bonjour", "pluralForm": null, "modifiedDate": "2021-03-19T17:32:54Z" } ] }
String prepublished (POST)
{ "projectId": "cef4c4151", "hashcode": "493c3a41b5e4648e26f6bbfb94d12341", "localeId": "fr-FR", "type": "string.localeCompleted", "publishStatus": "prepublished", "translations": [ { "translation": "bonjour", "pluralForm": null, "modifiedDate": "2021-03-19T17:32:54Z" } ] }