Integrating webhooks
Discover how you can listen to Transatel events by registering webhooks data stream
Through this guide you will learn how to
- Identify relevant events for your use cases and their payload definition
- Create a webhook endpoint accepting
HTTP POST JSONpayload - Handle Transatel events and execute your own logic
- Check event signature
- Register your webhook data stream for the selected events
- Test your integration by sending test events of your choice
- Activate your webhook data stream whenever you are ready
- Implement mTLS authentication
1. Identify relevant events
Start by reviewing all available Transatel events and identify which ones may be useful to trigger your logic:
Then review the specifications of those events that your endpoint will have to process.
2. Create a webhook endpoint
To receive events, you will need to create an HTTPS endpoint accepting POST requests of JSON payload.
This endpoint should in return respond with a HTTP/1.1 204 No content.
You can of course, implement this endpoint in the language / framework of your choice. Below a simple example in Java
using the Spark micro framework.
import static spark.Spark.port;import static spark.Spark.post;
public class WebhookEndpoint { public static void main(String[] args) {
port(8080);
post("/events", (request, response) -> { response.status(204); return ""; }); }}3. Handle events
Now that your endpoint is defined, you will need to add the event processing logic.
It's generally a 3 steps implementation:
- Parse the event and detect its type
- Execute / trigger your specific logic
- Returns a
HTTP/1.1 204 No contentresponse
Parse the event
Every Transatel event shares a common structure with an header holding information such as the eventId or the eventType and a body holding all other information specific to the event.
Below is a partial example of an event:
{ "header": { "eventId": "627e77fc-7694-4188-8f19-13ca3dbf8f51", "eventType": "OCS/PRODUCT/ACTIVATED", "eventDate": "2023-04-19T13:30:00Z" }, "body": { "mvnoRef": "<Customer account name>", "cos": "<Name of the class of service>", "msisdn": "33612345678", "iccid": "8988247076000000319", "externalReference": "SCOT78LH27", ... }}Parsing the header will allow you to detect which type of event you receive and as such determine which logic you have to trigger.
The body will also inform your of the detail of the event and more specifically which subscriber was concerned thanks to the msisdn and iccid fields.
Very important, when parsing events, always:
- declare only useful fields
- systematically ignore all unknown fields
This will guarantee your integration to be compatible with future backward compatible evolutions.
Execute your specific logic
Once you have detected which type of event you received and for which subscriber, you can execute your specific logic based on your use cases.
For complex logic, we recommend that you perform it asynchronously to avoid timeout issues.
Return a 204 response
Finally, your endpoint should respond with a HTTP/1.1 204 No content indicating that the event was well-received.
In case, your endpoint incorrectly responds with a KO response status (3xx, 4xx, 5xx) , the event delivery will be automatically retried.
Below is an example of implementation:
import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;import lombok.extern.slf4j.Slf4j;
import static spark.Spark.port;import static spark.Spark.post;
@Slf4jpublic class WebhookEndpoint { public static void main(String[] args) {
final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule());
port(8080);
post("/events", (request, response) -> {
final var event = objectMapper.readValue(request.body(), Event.class); final var eventType = event.getHeader().getEventType();
log.info("Received an {} event for subscriber: {}", eventType, event.getBody().getMsisdn());
switch (eventType) { case OCS_PRODUCT_ACTIVATED: // Execute your specific product activation logic break; case OCS_PRODUCT_EXPIRED: // Execute your specific product expiration logic break; //... }
response.status(204); return ""; }); }}Now that your endpoint is ready and before registering it as a webhook data stream, you will need to make it publicly accessible over HTTPS.
4. Check event signature (recommended)
As an extra step, we highly recommended that you secure your webhook data stream integration by verifying the signature sent with the events. This will guarantee that those events are originating from our platform and not from another server acting as Transatel.
To do so, two things are required:
- Define a secret when registering your webhook data stream
- Verify the signature of each event received on your endpoint
Define a secret on your webhook data stream
This is as easy as filling the field secret when creating your webhook data stream (See below for explanations on how to create your webhook data stream). This secret will then be used by our platform to sign all events sent to your webhook endpoint.
The signature will be provided as a X-TSL-Signature-256 HTTP header with the following definition: sha256=hex(HMACSHA256(payload))
(Example: sha256=a4ecb55b23be547f1b82e24f46beef8a0856675d85bd5baebd9ef0e3a05e7e3e)
Verify the payload signature
To verify, the signature you will need to adapt a little your endpoint implementation in order to compute the signature based on the secret you know and compare it to the signature received with the event.
Only if those signatures match you should accept the event and proceed further.
Below is the previous implementation example modified to include payload signature verification:
import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;import lombok.extern.slf4j.Slf4j;import org.apache.commons.codec.digest.HmacAlgorithms;import org.apache.commons.codec.digest.HmacUtils;import spark.Request;
import static spark.Spark.*;import static spark.utils.StringUtils.isEmpty;
@Slf4jpublic class WebhookEndpoint {
private static final String HEADER_HMAC_SIGNATURE = "X-TSL-Signature-256"; private static final String HMAC_PREFIX = "sha256="; private static final String SECRET = "my-secret";
public static void main(String[] args) {
final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule());
port(8080);
post("/events", (request, response) -> {
checkSignature(request);
final var event = objectMapper.readValue(request.body(), Event.class); final var eventType = event.getHeader().getEventType();
log.info("Received an {} event for subscriber: {}", eventType, event.getBody().getMsisdn());
switch (eventType) { case OCS_PRODUCT_ACTIVATED: // Execute your specific product activation logic break; case OCS_PRODUCT_EXPIRED: // Execute your specific product expiration logic break; //... }
response.status(204); return ""; }); }
private static void checkSignature(Request req) { final String requestSignature = req.headers(HEADER_HMAC_SIGNATURE);
if (isEmpty(requestSignature)) { log.error("Missing payload signature"); halt(403); }
final String sign = HMAC_PREFIX + new HmacUtils(HmacAlgorithms.HMAC_SHA_256, SECRET) .hmacHex(req.bodyAsBytes()); if (!sign.equals(requestSignature)) { log.error("Invalid request signature"); halt(403); } }}5. Register your webhook data stream
Go to the Transatel Console and:
- Click the
Add data streambutton under theData Streamtab - Select which events you want to receive
- Choose
Webhook endpointas destination type - Fill all the data stream information and click on the
Create data streambutton
Once done you should see your new data stream listed under the Data stream tab.
6. Test your integration
You are now ready to test your webhook data stream by simulating any subscribed event!
To do so, go to the Transatel Console and:
- Click on the data stream for which you want to simulate an event under the
Data streamtab - Click on the
Send stream eventbutton, then select the event type you want to simulate and click on theSendbutton
This operation can be used to:
- Troubleshoot communication issues between our platform and yours
- Validate the correct implementation on your webhook endpoint by simulating the different Transatel events
7. Activate your webhook data stream
Now that everything works great, let's activate your data stream and start receiving real events!
To do so, go to the Transatel Console and:
- Click on
...on the corresponding data stream you want to activate under theData streamtab - Activate by clicking on
Enable
8. Implement mTLS authentication (recommended)
As an extra step, we highly recommend that you secure your webhook data stream integration by implementing mTLS.
mTLS ensures that the parties at each end of a network connection are who they claim to be by verifying that they both have the correct private key.
To do so, two things are required:
- Implement Transatel certificate CA chain in your truststore
- Verify the CN client certificate received on your endpoint
Implement Transatel certificate CA chain in your truststore
Regardless of your HTTPS endpoint, you must implement a trusted list of certificate authorities. It’s often a PEM file containing a concatenated list of trusted CAs, base64 encoded.
Transatel use Sectigo CA as public trusted authority, with this complete chain:
- webservice-client.transatel.com - subject: CN = webservice-client.transatel.com
- Intermediate 1 - subject: C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Domain Validation Secure Server CA
- Intermediate 2 - subject: C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
- Root CA - subject: C = GB, ST = Greater Manchester, L = Salford, O = Comodo CA Limited, CN = AAA Certificate Services
Your truststore can be a concatenation from intermediate 1 to Root CA from certificates page.
At this point, the hand-check can be performed with all certificates signed by this chain.
Sample of nginx configuration:
server { listen 443 ssl default_server; ssl_verify_client on; ssl_client_certificate /path/to/trustore.pem; ssl_verify_depth 2; ...}Sample of Apache2 configuration:
<VirtualHost *:443> SSLVerifyClient require SSLCACertificateFile "/path/to/trustore.pem" SSLVerifyDepth 2 ...</VirtualHost>Verify the CN client certificate received on your endpoint
Now, at HTTP level, you can filter allowed connections to CN webservice-client.transatel.com client certificates.
With ngnix:
location / { if ($ssl_client_s_dn !~ ‘webservice-client.transatel.com’) { return 403;}With Apache2:
<Directory /var/www/> # or Location/Proxy ... Require expr "%{SSL_CLIENT_S_DN_CN} == 'webservice-client.transatel.com'" ...</Directory>What to do next?
Learn more about Transatel events by checking overviews: