Callbacks
A “callback” is a POST request sent from MT API to your Callback URL when an event you are already registered for occurs.
Example: When a document is translated with our MT API you get the document /:id needed to download.
Callback URLs must be accessible from public internet. Otherwise our servers won't be able to reach your servers.
Thanks to callbacks, you don't need to send constant requests to our API to check for updates on your translation, as you are directly informed when the translation is finished.
Callback signature
LanguageWire MT API signs the callback request it sends to your endpoints. This allows you to verify that the callbacks were sent by LanguageWire MT API and not by a third party. The way the signature is generated depends on which authorization method was used when the document translation was started.
Verifying signature for translations started with a JWT token
The callback request is authorized using a JWT (Json Web Token) token.
The token is included in the Authorization header of the callback request, with the "Bearer" prefix.
First you should verify that the token is valid (e.g. not expired) and issued by LanguageWire's IdP system.
The token is signed with an assymetric key using RS256 algorithm.
To validate the token, you have to first retrieve the public key from the IdP system.
The key is accessible at https://idp.languagewire.com/realms/languagewire, under the public_key property.
When you decode the token (you can use a tool like jwt.io to manually inspect a token), you must find the following properties along the JWT payload:
{
"iss": "https://idp.languagewire.com/realms/languagewire",
"signature": "payload_signature",
"exp": 1629352800,
"iat": 1629349200
}
- The
issproperty contains the issuer of the token. It must always behttps://idp.languagewire.com/realms/languagewire. - The
expandiatproperties are the expiration and issue time of the token, respectively. You should check that the token is not expired and that it was issued recently. - The
signatureproperty is the expected SHA-256 hash of the expected request body. You should compute the hash of the actual request body and compare it with the expected hash. If the actual hash does not match the expected hash, then the request body has been tempered with and the request must be rejected.
Do not modify or format the body of the request. Even if a single character is changed the HMAC will be different.
A short example of how this can be achieved in Python + Django + authlib:
import hmac
from authlib.jose import JsonWebToken
from django.http import HttpRequest
def verify_callback_signature(request: HttpRequest):
auth_header = request.headers["Authorization"]
if not auth_header.startswith("Bearer "):
# callback is invalid
raise ...
jwt_token = auth_header.removeprefix("Bearer ")
public_key = (
"-----BEGIN PUBLIC KEY-----\n"
+ <public_key> # retrieve public key here
+ "\n-----END PUBLIC KEY-----"
)
jwt = JsonWebToken(["RS256"])
jwt_claims = jwt.decode(
jwt_token,
key=public_key,
claims_options={
"exp": {
"essential": True,
},
"iat": {
"essential": True,
},
"iss": {
"essential": True,
"value": "https://idp.languagewire.com/realms/languagewire",
},
},
)
jwt_claims.validate()
expected_signature = jwt_claims["signature"]
actual_signature = hashlib.new("sha256", request.content).hexdigest()
if actual_signature == expected_signature:
# callback is valid
else:
# callback is invalid
Verifying signature for translations started with an API key
API keys are now deprecated and are only supported when using the v1 endpoints.
Check our migration guide on how you can transition to using v2 endpoints.
LanguageWire MT API signs the callback request it sends to your endpoints by including a signature X-Signature in each call's header.
This allows you to verify that the requests were sent by our API and not by a third party.
- The signature is generated using a HMAC-SHA256.
- It uses hexadecimal digits.
- Example of a signature:
50e03ebe65be98bb8bf11ba2c892d54c079aca2b0d3b0162769c6d757a25434f.
To determine the expected signature:
- Use your API Key as the hash key, and use the raw request body string as the message.
Do not modify or format the body of the request. Even if a single character is changed the HMAC will be different.
- Compare the signature in the header to the expected signature (in hexadecimal form).
-
To protect against timing attacks, use a constant-time string comparison to compare the expected signature.
-
Example of how this can be achieved in Python+Django:
import hmac
from django.http import HttpRequest
def verify_callback_signature(request: HttpRequest, secret_key: str):
signature = request.headers["x-signature"]
hash_obj = hmac.new(secret_key.encode(), request.body, "sha256")
actual_signature = hash_obj.hexdigest()
if hmac.compare_digest(actual_signature, signature):
# callback is valid
else:
# callback is invalid
Callback retries
Until a status code “200 OK Request has been successful” is received, MT API keeps retrying the request.
- There are 3 retries in total distributed at certain intervals (a "randomised value" delay between 0.5 to 1.5 seconds).
- If the maximum number of retries is reached, the error callback will be invoked with an error code instead.