Webhooks and Callbacks
WireMock can make asynchronous outbound HTTP calls when an incoming request is matched to a specific stub. This pattern is commonly referred to as webhooks or callbacks and is a common design in APIs that need to proactively notify their clients of events or perform long-running processing asynchronously without blocking.
Enabling webhooks #
Webhooks are provided via a WireMock extension, so this must be added when starting WireMock.
Java #
If you’re starting WireMock programmatically the webhooks extension must be added to your classpath.
Maven:
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-webhooks-extension</artifactId>
<version>2.35.1</version>
<scope>test</scope>
</dependency>
Gradle:
testCompile "org.wiremock:wiremock-webhooks-extension:2.35.1"
Then when creating the WireMockServer
or WireMockRule
the extension must be passed via the configuration object in the constructor:
@Rule
public WireMockRule wm = new WireMockRule(wireMockConfig().extensions(Webhooks.class));
Standalone #
To use the webhooks extension with standalone WireMock you must download the extension JAR file and add it to the Java classpath on the startup command line:
macOs / Linux: #
java -cp wiremock-jre8-standalone-2.35.1.jar:wiremock-webhooks-extension-2.35.1.jar \
com.github.tomakehurst.wiremock.standalone.WireMockServerRunner \
--extensions org.wiremock.webhooks.Webhooks
Windows: #
java -cp wiremock-jre8-standalone-2.35.1.jar;wiremock-webhooks-extension-2.35.1.jar \
com.github.tomakehurst.wiremock.standalone.WireMockServerRunner \
--extensions org.wiremock.webhooks.Webhooks
You can download the webhooks extension JAR here.
A simple, single webhook #
You can trigger a single webhook request to a fixed URL, with fixed data like this:
Java:
import static org.wiremock.webhooks.Webhooks.*;
...
wm.stubFor(post(urlPathEqualTo("/something-async"))
.willReturn(ok())
.withPostServeAction("webhook", webhook()
.withMethod(POST)
.withUrl("http://my-target-host/callback")
.withHeader("Content-Type", "application/json")
.withBody("{ \"result\": \"SUCCESS\" }"))
);
JSON:
{
"request": {
"urlPath": "/something-async",
"method": "POST"
},
"response": {
"status": 200
},
"postServeActions": [
{
"name": "webhook",
"parameters": {
"method": "POST",
"url": "http://my-target-host/callback",
"headers": {
"Content-Type": "application/json"
},
"body": "{ \"result\": \"SUCCESS\" }"
}
}
]
}
Using data from the original request #
Webhooks use the same templating system as WireMock responses. This means that any of the configuration fields can be provided with a template expression which will be resolved before firing the webhook.
Similarly to response templates the original request data is available, although in this case it is named originalRequest
.
Supposing we wanted to pass a transaction ID from the original (triggering) request and insert it into the JSON request body sent by the webhook call.
For an original request body JSON like this:
{
"transactionId": "12345"
}
We could construct a JSON request body in the webhook like this:
Java:
wm.stubFor(post(urlPathEqualTo("/templating"))
.willReturn(ok())
.withPostServeAction("webhook", webhook()
.withMethod(POST)
.withUrl("http://my-target-host/callback")
.withHeader("Content-Type", "application/json")
.withBody("{ \"message\": \"success\", \"transactionId\": \"{{jsonPath originalRequest.body '$.transactionId'}}\" }")
);
JSON:
{
"request": {
"urlPath": "/templating",
"method": "POST"
},
"response": {
"status": 200
},
"postServeActions": [
{
"name": "webhook",
"parameters": {
"method": "POST",
"url": "http://my-target-host/callback",
"headers": {
"Content-Type": "application/json"
},
"body": "{ \"message\": \"success\", \"transactionId\": \"{{jsonPath originalRequest.body '$.transactionId'}}\" }"
}
}
]
}
note
Webhook templates currently do not support system or environment variables.
Implementing a callback using templating #
To implement the callback pattern, where the original request contains the target to be called on completion of a long-running task, we can use templating on the URL and method.
Java:
wm.stubFor(post(urlPathEqualTo("/something-async"))
.willReturn(ok())
.withPostServeAction("webhook", webhook()
.withMethod("{{jsonPath originalRequest.body '$.callbackMethod'}}")
.withUrl("{{jsonPath originalRequest.body '$.callbackUrl'}}"))
);
JSON:
{
"request": {
"urlPath": "/something-async",
"method": "POST"
},
"response": {
"status": 200
},
"postServeActions": [
{
"name": "webhook",
"parameters": {
"method": "{{jsonPath originalRequest.body '$.callbackMethod'}}",
"url": "{{jsonPath originalRequest.body '$.callbackUrl'}}"
}
}
]
}
Adding delays #
A fixed or random delay can be added before the webhook call is made, using the same style of delay parameters as stubs.
Fixed delays #
Java:
wm.stubFor(post(urlPathEqualTo("/delayed"))
.willReturn(ok())
.withPostServeAction("webhook", webhook()
.withFixedDelay(1000)
.withMethod(RequestMethod.GET)
.withUrl("http://my-target-host/callback")
)
);
JSON:
{
"request": {
"urlPath": "/delayed",
"method": "POST"
},
"response": {
"status": 200
},
"postServeActions": [
{
"name": "webhook",
"parameters": {
"method": "GET",
"url": "http://my-target-host/callback",
"delay": {
"type": "fixed",
"milliseconds": 1000
}
}
}
]
}
Random delays #
Java:
wm.stubFor(post(urlPathEqualTo("/delayed"))
.willReturn(ok())
.withPostServeAction("webhook", webhook()
.withDelay(new UniformDistribution(500, 1000))
.withMethod(RequestMethod.GET)
.withUrl("http://my-target-host/callback")
)
);
JSON:
{
"request": {
"urlPath": "/delayed",
"method": "POST"
},
"response": {
"status": 200
},
"postServeActions": [
{
"name": "webhook",
"parameters": {
"method": "GET",
"url": "http://my-target-host/callback",
"delay": {
"type": "uniform",
"lower": 500,
"upper": 1000
}
}
}
]
}