Skip to main content

Webhooks (callbacks)

A “webhook” is a POST request sent from the Project API to your service defined in a "Callback URL" when there is an update on the status of the file you have sent us to translate.

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 or cancelled.

Using webhooks

If you want LanguageWire Project API to notify your integration service when the translations are ready you just need to add the callbackUrl as a header in your "add file" request (see add source file endpoint).

The "callbackUrl" needs to be HTML encoded to ensure special characters are escaped.

The payload you will receive

Project API will call the URL you introduced using the 'POST' method with a payload similar to the following:

{
"ProjectId": 123,
"TranslationId": 456,
"SourceFileId": "0fc7b7aa-1667-4297-8a90-f61081bdff4f",
"SourceFileName": "a.txt",
"TargetFileId": "dc4a9568-162d-40c6-b9b5-99d8f41f1201",
"TargetFileName": "a (en-GB).txt",
"SourceLanguageCode": "es-ES",
"TargetLanguageCode": "en-GB",
"Status": "Finished"
}

The "ProjectId" field contains the Id of the project where the file has been added to.
The "TranslationId" field corresponds to the combination of files that have changed their status. Multiple files from the same project might have changed their status and if this is the case, you will receive one callback per each of the files, but all with the same translationId.
The "SourceFileId" is the ID of the file you uploaded for us to translate.
The "SourceFileName" is the name of the file.
The "TargetFileId" is the ID of the target file created with the requested translation. You need to use this ID to download the translation using the download file endpoint. The "TargetFileName" is the name of the target file. The "SourceLanguageCode" indicates the language of the original file. The "TargetLanguageCode" indicates the language of the translated file. The field "Status" can only be either "FINISHED" or "CANCELLED".

Webhooks security aspects

When receiving a webhook, you should verify the call is legit by checking the following aspects:

Verify authorization header

The callback request is authorized using a JWT (Json Web Token) token. The token is included in the Authorization header of the callback request.

When you uncode the token (you can use a tool like jwt.io), you must find the following properties along the payload:

{
"iss": "https://idp.languagewire.com/realms/languagewire",
"signature": "payload_signature",
"exp": 1629352800,
"iat": 1629349200
}
  • The iss property contains the issuer of the token. It must always be https://idp.languagewire.com/realms/languagewire.
  • The exp and iat properties are the expiration and issue time of the token, respectively. You need to check that the token is not expired and that it was issued recently.
  • The signature property is the signature of the token. You need to verify that the signature is valid using the public key provided by LanguageWire.

Verify the signature

  • The signature is generated using a HMAC, which stands for Hash-based message authentication code.

  • Along with its cryptographic SHA256 hash function, it represents higher security than any other authentication code.

  • It uses hexadecimal digits.

  • Example of a signature: 03056707F3918651FC7B2AACEC8CF5C6830E1C24A03215CA3F74321C384E2F9D.

  • Code examples to verify the signature:

C#

// payload received without line breaks or empty spaces
var payload = """{"ProjectId":2503057,"TranslationId":3049394,"SourceFileId":"d12b3b94-f46b-4783-a29c-07c062b17db4","SourceFileName":"test.txt","TargetFileId":"90a258c8-4d30-48c4-8e0c-8e849fcfbcc5","TargetFileName":"test(spa-ES).txt","SourceLanguageCode":"en-GB","TargetLanguageCode":"es-ES","Status":"Finished"}""";
var payloadBytes = Encoding.ASCII.GetBytes(payload);
var hashedPayloadBytes = SHA256.Create().ComputeHash(payloadBytes);
var shouldBeSignature = BitConverter.ToString(hashedPayloadBytes).Replace("-", string.Empty);
Console.WriteLine("Signature should be: " + shouldBeSignature);

Python

import hashlib

payload = """{"ProjectId":2503057,"TranslationId":3049394,"SourceFileId":"d12b3b94-f46b-4783-a29c-07c062b17db4","SourceFileName":"test.txt","TargetFileId":"90a258c8-4d30-48c4-8e0c-8e849fcfbcc5","TargetFileName":"test(spa-ES).txt","SourceLanguageCode":"en-GB","TargetLanguageCode":"es-ES","Status":"Finished"}"""
payload_bytes = payload.encode('ascii')
hashed_payload_bytes = hashlib.sha256(payload_bytes).digest()
should_be_signature = hashed_payload_bytes.hex()
print("Signature should be: " + should_be_signature)

Java

String payload = "{\"ProjectId\":2503057,\"TranslationId\":3049394,\"SourceFileId\":\"d12b3b94-f46b-4783-a29c-07c062b17db4\",\"SourceFileName\":\"test.txt\",\"TargetFileId\":\"90a258c8-4d30-48c4-8e0c-8e849fcfbcc5\",\"TargetFileName\":\"test(spa-ES).txt\",\"SourceLanguageCode\":\"en-GB\",\"TargetLanguageCode\":\"es-ES\",\"Status\":\"Finished\"}";
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(payload.getBytes(StandardCharsets.UTF_8));
String shouldBeSignature = bytesToHex(hash);
System.out.println("Signature should be: " + shouldBeSignature);

private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}

Node.js

const crypto = require('crypto');

let payload = `{"ProjectId":2503057,"TranslationId":3049394,"SourceFileId":"d12b3b94-f46b-4783-a29c-07c062b17db4","SourceFileName":"test.txt","TargetFileId":"90a258c8-4d30-48c4-8e0c-8e849fcfbcc5","TargetFileName":"test(spa-ES).txt","SourceLanguageCode":"en-GB","TargetLanguageCode":"es-ES","Status":"Finished"}`;
let hash = crypto.createHash('sha256').update(payload, 'utf8').digest('hex');
console.log("Signature should be: " + hash);

PHP

$payload = '{"ProjectId":2503057,"TranslationId":3049394,"SourceFileId":"d12b3b94-f46b-4783-a29c-07c062b17db4","SourceFileName":"test.txt","TargetFileId":"90a258c8-4d30-48c4-8e0c-8e849fcfbcc5","TargetFileName":"test(spa-ES).txt","SourceLanguageCode":"en-GB","TargetLanguageCode":"es-ES","Status":"Finished"}';
$hash = hash('sha256', $payload);
echo "Signature should be: " . $hash;
Tip

Do not modify or format the body of the request. Even if a single character is changed the HMAC will be different.


Callback retries (to verify)

Until a status code “200 OK Request has been successful” is received, Project APIs keeps retrying the request.

  • There are 10 retries in total distributed in 3 days.

Benefits of using webhooks

Webhooks are a powerful tool for integrating systems and automating processes.

Some benefits:
✔️ No need for polling: Reducing time and saving resources for the client app.
✔️ Quick set up: You just need to set basic parameters.
✔️ Automate data transfer: The payload is sent as soon as the event occurs on the server app, hence is real-time data exchange.
✔️ Lightweight payloads: Webhooks deal with small amounts of information between 2 endpoints, often in the form of a notification.

Note

A webhook expires after 6 months.