Webhooks
Transifex lets you specify a webhook and get notified whenever a target language of a resource has fully been translated, reviewed or proofread, as well as when translation fill ups are done. This way, you can pull translations as soon as they are ready without having to constantly check Transifex for updates.
Adding webhooks to a project
Note
For webhooks to work, you'll need a web server to listen for the webhook calls and an application to react to those. Note that webhooks are triggered automatically in Transifex.
Webhooks can be added to any project in an Organization. To do this:
- From the Dashboard, head to the project you want to set up webhooks for.
- Click on Settings. Only Project Maintainers and Organization Admins are able to access this menu.
- In the submenu, click on Webhooks.
- Click on Add webhook.
- In the popup, enter your webhook URL and secret key (the secret key is optional). Then choose whether you'd like to be notified about all events, or only a specific one.
The callback URL should listen for a POST request with the following variables:
- project: The slug of the project this notification is for.
- resource: The slug of the resource this notification is for.
- language: The language code of the translation that was modified.
- translated: An integer that represents the completion percentage of translations for the particular resource in the particular language. If a resource is fully reviewed or proofread in a particular language, a variable named reviewed or proofread respectively will be used instead of the translated one.
- event: A string that describes the webhook type, which you can use to filter webhooks. The possible values are:
translation_completed: When a target language of a resource is 100% translated, then the webhook will be triggered. The response will look like this:
{
"project": "project_slug",
"translated": 100,
"resource": "resource_slug",
"event": "translation_completed",
"language": "lang_code"
}
review_completed: When a target language of a resource is 100% reviewed, then the webhook will be triggered. The response will look like this:
{
"resource": "resource_slug",
"language": "lang_code",
"reviewed": 100,
"project": "project_slug",
"is_final": true/false,
"event": "review_completed"
}
Note: The parameter "is_final" is false, if the second review step (proofread) has been enabled and only the first review step has been completed.
proofread_completed: When a target language of a resource is 100% proofread, then the webhook will be triggered. The response will look like this:
{
"resource": "resource_slug",
"language": "lang_code",
"reviewed": 100,
"project": "project_slug",
"is_final": true/false,
"event": "review_completed"
}
fillup_completed: When TM or MT fill up tasks are completed.
{
"machine translation": 0,
"resource": "resource_slug",
"language": "lang_code",
"project": "project_slug",
"translated": 50,
"translation memory": 50,
"event": "fillup_completed"
}
translations_updated: When a 100% translated resource has a translation updated (edited).
{
"project": "project_slug",
"translated": 100.0,
"resource": "resource_slug",
"event": "translation_completed_updated",
"language": "lang_code"
}
- When you're done, click Save changes.
When the event(s) you specified happens, an update is fired via a POST request from Transifex to the provided URL. The body of the post will be a JSON payload of the variables mentioned above.
Headers
If you've defined a secret key, then each webhook will include two headers:
- X-TX-Signature: This is the computed signature generated by Transifex. It's used to tell whether the request is valid or not.
- User-Agent: Transifex itself.
Verifying a webhook
The latest version of the Transifex webhook provides an extensible way to notify third-party services of changes in the progress of a resource in Transifex.
To validate that the webhook is coming from Transifex, you need to first set a shared secret with Transifex. This will be used to calculate the webhook signature. When the webhook is received, we then calculate the signature on our end and check if it matches the submitted signature.
To calculate the signature, you need to concatenate (one element per line) the following:
- The method type, e.g. POST
- The URL path where your webhook listener listens
- The submission date (taken from the Date header)
- The hash of the contents of the webhook calculated through md5
After that, the signature is extracted by encrypting the concatenated data with the SHA256 algorithm using the shared secret. Finally, we encode the calculated hash in Base64.
Here's a sample of the webhook content:
{
"project": <project slug>,
"translated": <completion percentage>,
"resource": <resource slug,
"event": "translation_completed" || “review_completed” || “proofread_completed“ || “fillup_completed”,
"language": <language_code>
}
Below are a series of code samples that demonstrate the required validation procedure.
PHP
$http_verb = 'POST';
$received_json = file_get_contents("php://input", TRUE);
$webhook_sig = $_SERVER['X-TX-Signature-V2'];
$http_url_path = $_SERVER['X-TX-Url'];
$http_gmt_date = $_SERVER['Date'];
$content_md5 = md5($received_json);
$msg = join(PHP_EOL, array('POST', $http_url_path, $http_gmt_date, $content_md5));
$sig = base64_encode(hash_hmac('sha256', $msg, TRANSIFEX_SECRET, true));
return $sig == $webhook_sig
Node
const sign_v2 = (url, date, data, secret) => {
const content_md5 = md5(data);
const msg = ['POST', url, date, content_md5].join('\n');
const hmac = crypto.createHmac('sha256', secret);
return hmac.update(msg).digest().toString('base64');
};
Python
import base64
import hmac
import hashlib
# Both http_gmt_date and http_url_path can be found through the response headers namely in the HTTP_DATE and HTTP_X_TX_URL headers respectively
http_verb = 'POST'
http_url_path = 'http://www.test.com/page/'
http_gmt_date = 'Wed, 08 Feb 2017 09:49:18 GMT'
secret = 'secret_key'
response_payload = '{"project": "project-slug", "translated": 100.0, "resource": "resource-slug", "event": "translation_completed", "language": "de"}'
content_md5 = hashlib.md5(response_payload).hexdigest()
msg = b'\n'.join([
http_verb, http_url_path, http_gmt_date, content_md5
])
tx_signature = base64.b64encode(
hmac.new(
key=secret,
msg=msg,
digestmod=hashlib.sha256
).digest()
)
Ruby
require 'openssl'
require 'base64'
HMAC_DIGEST_256 = OpenSSL::Digest.new('sha256')
http_verb = 'POST'
http_date = 'Fri, 17 Feb 2017 08:24:07 GMT'
url = 'http://www.transifex.com/'
secret = 'some secret'
content = '{"project": "project-slug", "translated": 100, "resource": "resource-slug", "event": "translation_completed", "language": "de"}'
content_md5 = Digest::MD5.hexdigest content
data = [http_verb, url, date, content_md5].join("\n")
Base64.encode64(
OpenSSL::HMAC.digest(HMAC_DIGEST_256, secret, data)
).strip
Former way of verifying a webhook
This method is deprecated.
You can verify a webhook is coming from Transifex by calculating a digital signature. Each webhook request contains an X-TX-Signature header that's generated using the given secret key, along with the payload data sent in the request. To verify if a request came from Transifex, compute the HMAC hash and compare it to the header value sent in the request. If the computed signatures match, you can be sure the request was sent from Transifex.
CALCULATING AN HMAC-SHA1 SIGNATURE IN PYTHON
import base64
import hmac
import hashlib
data = {'project':u'your_project_slug', 'reviewed':100, 'resource':u'your_resource_slug', 'language':u'language_code', 'event':u'review_completed'}
data = str(data)
h=hmac.new(key='your_secret_key', msg=data, digestmod=hashlib.sha1)
h.hexdigest()
'b18b4a02ac486d7f0b9fe93387662dc9f7a1ea06' --> This is how the result will look like
base64.b64encode(h.digest())
'sYtKAqxIbX8Ln+kzh2Ytyfeh6gY==' --> The X-TX-Signature will be finally returned
EXAMPLE WEBHOOK RESPONSE TO THE URL SUBMITTED TO TRANSIFEX
{
"project": "project_slug",
"resource": "resource_slug",
"language": "language_code",
"translated": 100,
"event": "translation_completed"
}
Webhook notifications
The full list of IP addresses that webhook notifications may come from is:
- 52.210.102.183
- 99.80.131.68
- 99.80.229.48
Updated over 2 years ago