apps/docs/content/guides/integrate/actions/testing-request-signature.mdx
import JsonGo from './go/json.go'; import JwtGo from './go/jwt.go'; import JweGo from './go/jwe.go'; import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
This guide shows you how to verify the integrity of the received data on your target. There are three options available, which will be demonstrated in the following sections. The examples are based on the request action, but the same principles apply to other action types as well.
Before you start, make sure you have everything set up correctly.
ZITADEL supports the following three types of payload in actions:
We'll start the endpoint on port '8090' and if we want to use it as webhook, the target can be created as follows:
See Create a target for more detailed information.
Specify the payloadType according to the implementation you want to test: PAYLOAD_TYPE_JSON, PAYLOAD_TYPE_JWT, or PAYLOAD_TYPE_JWE.
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/actions/targets' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
--data-raw '{
"name": "local webhook",
"restWebhook": {
"interruptOnError": true
},
"endpoint": "http://localhost:8090/webhook",
"timeout": "10s",
"payloadType": "PAYLOAD_TYPE_JSON"
}'
Example response after creating the target:
{
"id": "344649040681500814",
"creationDate": "2025-10-31T15:00:36.432595dZ",
"signingKey": "somekey"
}
Save the returned ID to set in the execution. If you're intending to use the PAYLOAD_TYPE_JSON, additionally store the signingKey and use it in the example above.
If you are using the PAYLOAD_TYPE_JWE, you need to provide a public key to ZITADEL so that it can encrypt the payload.
Create a public/private key pair. You can use the following command to generate an RSA key pair:
openssl genpkey -algorithm RSA -outform PEM -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
Then upload the public key to ZITADEL using the following command. Replace <TargetID> with the ID of the target you created earlier.
Use the base64 encoded content of the public_key.pem file as the value for publicKey.
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/actions/targets/<TargetID>/publickeys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
--data-raw '{
"publicKey": "<base64 encoded contents of public_key.pem>"
}'
Be sure to also activate the public key for the target using the returned <KeyID> from the previous request:
curl -L -X PUT 'https://${CUSTOM_DOMAIN}/v2/actions/targets/<TargetID>/publickeys/<KeyID>/activate' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>'
To configure ZITADEL to call the target when an API endpoint is called, you need to set an execution and define the request condition.
See Set an execution for more detailed information.
curl -L -X PUT 'https://${CUSTOM_DOMAIN}/v2/actions/executions' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
--data-raw '{
"condition": {
"request": {
"method": "/zitadel.user.v2.UserService/CreateUser"
}
},
"targets": [
"<TargetID returned>"
]
}'
To test the actions feature, you need to create a target that will be called when an API endpoint is called. You will need to implement a listener that can receive HTTP requests. For this example, we will use a simple Go HTTP server that will print the received request to standard output. As mentioned before, this validation can and should be applied to any target implementation.
<Tabs> <Tab value="JSON"> <DynamicCodeBlock lang="go" code={JsonGo} /> </Tab> <Tab value="JWT"> <DynamicCodeBlock lang="go" code={JwtGo} /> </Tab> <Tab value="JWE"> <DynamicCodeBlock lang="go" code={JweGo} /> </Tab> </Tabs> <Callout type="info"> The example above runs only on your local machine (`localhost`). To test it with Zitadel, you must make your listener reachable from the internet. You can do this by using **Webhook.site** (see [Creating a Listener with Webhook.site](./webhook-site-setup)). </Callout>Now that you have set up the target and execution, you can test it by creating a user through the Management Console UI or by calling the ZITADEL API to create a user.
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/users/new' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
--data-raw '{
"organizationId": "344648897353810062",
"human":
{
"profile":
{
"givenName": "Minnie",
"familyName": "Mouse",
"nickName": "Mini",
"displayName": "Minnie Mouse",
"preferredLanguage": "en",
"gender": "GENDER_FEMALE"
},
"email":
{
"email": "[email protected]"
}
}
}'
Your server should now print out something like the following. Check out the Sent information Request payload description.
{
"fullMethod": "/zitadel.user.v2.UserService/CreateUser",
"instanceID": "344648897353744526",
"orgID": "344648897353810062",
"projectID": "344648897353875598",
"userID": "344648897354465422",
"request":
{
"organizationId": "344648897353810062",
"human":
{
"profile":
{
"givenName": "Minnie",
"familyName": "Mouse",
"nickName": "Mini",
"displayName": "Minnie Mouse",
"preferredLanguage": "en",
"gender": "GENDER_FEMALE"
},
"email":
{
"email": "[email protected]"
}
}
},
"headers":
{
"Content-Type":
[
"application/grpc"
],
"Host":
[
"localhost:8080"
],
"X-Forwarded-For":
[
"::1"
],
"X-Forwarded-Host":
[
"localhost:8080"
]
}
}
You have successfully set up a target and verified the payload integrity for request actions using your preferred payload type. You can now extend this setup to other action types and integrate it into your workflows as needed. Selecting the appropriate payload type ensures that your data is transmitted securely and can be validated effectively on the receiving end. Find more information about the actions feature in the API documentation.