Requirements in Masnet
Before configuring the receiving system, prepare the destination in Masnet. The steps are the same for every system.
- Open Company › Integrations (the Spanish interface labels it Empresa › Integraciones), go to the Destinations tab and select New destination. If the integration has no authorized data yet, Masnet asks you to authorize it first.
- In Destination address (https) enter the receiver public URL. It must be
https, port 443 or 8443 and a public DNS name: user information in the URL, fragments, IP addresses and internal names are not accepted. A query string is allowed; Masnet treats the whole URL as a credential. - In Format choose Standard JSON (
application/json) or Structured CloudEvents (application/cloudevents+json). The event content is the same; see Webhooks. - Select Create destination. Masnet shows the signing secret (
whsec_…) only once. Store it immediately in the receiver secret store; it cannot be read again. - Select Test destination and then Send test. The integration is activated only after a successful test; then use Activate destination.
iFlow configuration
- Create an iFlow and use an HTTPS sender adapter.
- In the address parameter enter a path of your own, for example
/masnet/events. The runtime URL has the formhttps://<runtime host>/http/masnet/events; that full URL is what you enter in Destination address (https) in Masnet. - In the authorization parameter choose an SAP method compatible with one of the methods Masnet can send (Basic, Bearer, OAuth 2.0 client credentials or mTLS) and configure the same method in the Masnet destination credentials. The signature is always sent, with any method.
- Leave CSRF protection disabled: Masnet does not fetch a token beforehand. Confirm the behavior in the documentation of your version.
- Add a Script step immediately after the adapter, before any converter or mapping. Paste the script from the next section.
- Load the signing secret as an SAP secure parameter and assign it to the message property
webhook_secretbefore the script. Do not write it inside the script.
Signature verification
The signature is v1, followed by base64(HMAC-SHA256) computed with the binary key obtained by base64-decoding what follows whsec_, over the bytes webhook-id + . + webhook-timestamp + . + the exact body. Always verify over the received bytes, before parsing the JSON.
The script reads webhook-id, webhook-timestamp and webhook-signature from the message headers and the body as byte[]. It rejects events more than 5 minutes off and accepts any of the signatures in the header.
import com.sap.gateway.ip.core.customdev.util.Message
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.security.MessageDigest
// Script step of the iFlow. The secret is read from a Secure Parameter alias or property, never hardcoded.
Message processData(Message message) {
def headers = message.getHeaders()
String id = headers.get('webhook-id')
String timestamp = headers.get('webhook-timestamp')
String signatures = headers.get('webhook-signature')
String secret = message.getProperty('webhook_secret')
byte[] body = message.getBody(byte[]) // raw bytes, before any JSON mapping
boolean ok = false
if (id && timestamp ==~ /\d+/ && signatures && Math.abs(System.currentTimeMillis().intdiv(1000) - Long.parseLong(timestamp)) <= 300) {
byte[] key = Base64.decoder.decode(secret.replaceFirst('^whsec_', ''))
Mac mac = Mac.getInstance('HmacSHA256')
mac.init(new SecretKeySpec(key, 'HmacSHA256'))
mac.update("${id}.${timestamp}.".getBytes('UTF-8'))
byte[] expected = mac.doFinal(body)
// Two signatures separated by a space while the secret is rotating; any match is valid.
ok = signatures.split(' ').any { part ->
def pair = part.split(',', 2)
if (pair.length != 2 || pair[0] != 'v1') return false
try {
return MessageDigest.isEqual(Base64.decoder.decode(pair[1]), expected)
} catch (IllegalArgumentException ignored) {
return false // malformed entry: ignore it and try the next one
}
}
}
if (!ok) throw new SecurityException('Invalid Masnet webhook signature') // the iFlow answers with an error status
message.setProperty('signature_verified', 'true')
return message
}Response
If the script ends without an exception, the iFlow answers with success (2xx). If it throws SecurityException, the iFlow answers with an error status and Masnet retries; this is expected for an invalid signature. Keep downstream processing decoupled (for example, with the messaging mechanism of your iFlow) so that you answer well within 15 seconds.
Testing with the test event
- Deploy the iFlow.
- In Masnet, select Test destination and Send test.
- Check in the SAP message monitor that the message arrived with
Masnet-Event-Type: com.masnet.integration.test.v1and thatsignature_verifiedistrue. - In Masnet, the result shows the response code and the measured times.
Operation: duplicates and ordering
- Idempotency.
webhook-ididentifies the event and is kept across retries and redeliveries. Record the identifiers already processed and discard repeats with a 2xx response. - Ordering. Each resource (
subject) carries an increasingsequence. Discard an event whosesequenceis lower than or equal to the last one applied for thatsubject. Events of different resources have no order between them. - Retries. Masnet retries any non-2xx response for up to 72 hours; the full schedule is in Webhooks.
- Secret rotation. When rotating with overlap, the previous secret stays valid for 24 hours and
webhook-signaturecarries twov1,…signatures separated by a space. Accept the event if either one matches.
Troubleshooting
Masnet records the response code of each attempt in the delivery history. These are the effects of each receiver response:
| Receiver response | Effect in Masnet |
|---|---|
| 2xx | Successful delivery. It means receipt, not business processing. |
| 3xx | The redirect is not followed. It counts as a failure and is retried; the history shows the Location without its query string. |
| 400, 401, 403, 404 and other 4xx or 5xx | Retryable failure for up to 72 hours from the start of the delivery cycle. |
| 410 | Masnet disables the destination and cancels pending deliveries. Return 410 only if the destination must stop receiving events. |
| 413 | The event is marked as undeliverable to that destination and is not retried. |
429 or 503 with Retry-After | Retry-After (seconds or HTTP date) is honored up to a maximum of 1 hour. |
| No response within 15 seconds | Timeout (5 s to connect, 15 s in total). It is a retryable failure: answer 2xx and process asynchronously. |
| Script error because of an invalid signature | Check the secret, that the script receives the original bytes, and the server clock (5-minute tolerance). |