Implement an external service for flow extensions¶
A flow extension calls an external HTTP service that you build and host. This service is where your custom logic runs. Use it to enrich user claims during registration, validate input against an external system, derive computed claims, or stop a flow with a business-rule failure.
This guide explains the contract your service must implement so that WSO2 Identity Server can integrate with it. You can build the service in any language or framework you prefer.
How it works¶
At the flow extension step of a flow, WSO2 Identity Server pauses the flow and sends an HTTP POST request with a JSON envelope to your service. Your service runs its logic and responds with an actionStatus that determines what happens next:
| Status | Meaning |
|---|---|
SUCCESS |
Apply the returned operations and continue the flow. |
FAILED |
Stop the flow for a business or policy reason (for example, "user not allowed"). WSO2 Identity Server surfaces this to the end user as a flow error. |
ERROR |
Your service hit a processing or server-side error (a crash or a downstream failure). WSO2 Identity Server returns a 500 to the client. |
Each request includes an allowedOperations whitelist that defines exactly which changes your service may make. If your response references anything outside this whitelist, WSO2 Identity Server rejects it.
Authentication¶
Configure the authentication scheme WSO2 Identity Server uses for the outbound call when you create the flow extension. Your service should validate the incoming credential on every request and reject unauthenticated calls.
Request reference¶
WSO2 Identity Server sends the following request to your service.
The request uses the following HTTP contract:
- Method:
POST - Content-Type:
application/json - Timeout: 3 seconds
Top-level envelope¶
| Field | Type | Description |
|---|---|---|
actionType |
string | Always FLOW_EXTENSION for this integration. |
event |
object | A snapshot of the flow state. See event object. |
allowedOperations |
array | The whitelist of changes your service may emit. See allowedOperations. |
requestId |
string | A unique identifier for this invocation. |
{
"actionType": "FLOW_EXTENSION",
"event": { ... },
"allowedOperations": [ ... ],
"requestId": "93c2fb70-6f8c-444b-8ff8-36ff580dabb7"
}
Event object¶
The event object carries context about the flow and the organization.
| Field | Type | Description |
|---|---|---|
flow |
object | The flow being executed. See flow object. |
application |
object | The application context. See application object. |
Flow object
| Field | Type | Description |
|---|---|---|
flowType |
string | The flow being executed. |
flowId |
string | The identifier for this single flow execution. |
portalUrl |
string | The URL of the WSO2 Identity Server portal page. |
user |
object | The user being acted on. See user object. |
{
"flowType": "REGISTRATION",
"flowId": "d6e02342-7c5b-40de-a3f9-403ae5d163a9",
"portalUrl": "https://accounts.asgardeo.io/t/<your-organization-name>/accounts/register",
"user": { ... }
}
User object
| Field | Type | Description |
|---|---|---|
claims |
array | The current claims, each as { uri, value }. |
{
"claims":[
{
"uri":"http://wso2.org/claims/givenname",
"value":"John"
},
{
"uri":"http://wso2.org/claims/mobile",
"value":"0123456789"
}
]
}
Application object
| Field | Type | Description |
|---|---|---|
id |
string | The application UUID. |
Note
The application.id value is only available for flows initiated with the applicationId parameter.
Allowed operations¶
Each entry defines a change your service is permitted to make. If your response references anything outside this whitelist, WSO2 Identity Server rejects it.
The only operation in FLOW_EXTENSION is replace, which creates or replaces a claim at one of the listed paths:
- If the value already exists in the flow, its value is replaced.
- If no value exists in the flow, WSO2 Identity Server adds the given value.
- If the supplied value is an empty string (
"") ornull, the claim value is set to empty.
You may only target paths listed in paths.
{
"op": "replace",
"paths": [
"/user/claims[uri=http://wso2.org/claims/identifier]",
"/user/claims[uri=http://wso2.org/claims/tier]"
]
}
Response reference¶
WSO2 Identity Server distinguishes outcomes by the HTTP status of your response:
- HTTP 200 OK: Used for
SUCCESSandFAILEDresponses. TheactionStatusin the body tells WSO2 Identity Server which. - HTTP 400, 401, or 500: Interpreted as
ERROR. When your service returns an error status or fails to respond entirely, WSO2 Identity Server treats it as a failure to execute the action and aborts the flow.
The response body has the following shape:
Replace operation¶
The path must match one of the paths listed against the replace entry in allowedOperations. The same create or replace behavior applies: the value is created if absent and set to empty if value is empty.
{
"op": "replace",
"path": "/user/claims[uri=http://wso2.org/claims/customClaim]",
"value": "new-value"
}
Success response¶
Apply the returned operations and continue the flow. operations is optional; omit it when there is nothing to change but the flow should still proceed.
{
"actionStatus": "SUCCESS",
"operations": [
{
"op": "replace",
"path": "/user/claims[uri=http://wso2.org/claims/customClaim]",
"value": "new-value1"
},
{
"op": "replace",
"path": "/user/claims[uri=http://wso2.org/claims/multiValuedClaim]",
"value": ["multi-value1", "multi-value2"]
}
]
}
Failed response¶
Use FAILED when your service decides, on business or policy grounds, that the flow should not continue. For example, the user is on a deny list, or the supplied data failed an external check. The end user sees a flow-level error.
| Field | Type | Description |
|---|---|---|
actionStatus |
string | FAILED |
failureReason |
string | A short, user-facing reason for the failure. Or send an i18n key. |
failureDescription |
string | A longer, user-facing description of the failure. Or send an i18n key. |
{
"actionStatus": "FAILED",
"failureReason": "User not allowed",
"failureDescription": "You are currently restricted from creating new accounts."
}
The end user sees the failureReason and failureDescription rendered as a flow error:
Error response¶
Use ERROR when your service itself hits a processing or server error (a downstream call failed, an unexpected exception was caught, and so on). Return HTTP 400, 401, or 500. WSO2 Identity Server treats this as a failure to execute the action and aborts the flow.
| Field | Type | Description |
|---|---|---|
actionStatus |
string | ERROR |
errorMessage |
string | A short error message. Published to diagnostic logs. |
errorDescription |
string | A longer description of the error. Published to diagnostic logs. |
{
"actionStatus": "ERROR",
"errorMessage": "External service failure",
"errorDescription": "The external service met with an unexpected error."
}
Avoid exposing personal data in error messages
Don't include personally identifiable information (PII) in failureReason, failureDescription, errorMessage, or errorDescription. If you must include such data, mask it.
Example¶
The following example shows a self-registration flow where your service may replace two claims.
WSO2 Identity Server sends the following request:
{
"actionType":"FLOW_EXTENSION",
"event":{
"flow":{
"flowType":"REGISTRATION",
"flowId":"d6e02342-7c5b-40de-a3f9-403ae5d163a9",
"user":{
"claims":[
{
"uri":"http://wso2.org/claims/givenname",
"value":"John"
},
{
"uri":"http://wso2.org/claims/multiValuedClaim",
"value":[
"value1",
"value2"
]
},
{
"uri":"http://wso2.org/claims/customClaim",
"value":"customValue1"
}
]
}
}
},
"allowedOperations":[
{
"op":"replace",
"paths":[
"/user/claims[uri=http://wso2.org/claims/multiValuedClaim]",
"/user/claims[uri=http://wso2.org/claims/customClaim]"
]
}
],
"requestId":"93c2fb70-6f8c-444b-8ff8-36ff580dabb7"
}
A response that applies a claim and continues the flow:
{
"actionStatus": "SUCCESS",
"operations": [
{
"op": "replace",
"path": "/user/claims[uri=http://wso2.org/claims/customClaim]",
"value": "123"
}
]
}
A response that fails the flow:
{
"actionStatus": "FAILED",
"failureReason": "User not allowed",
"failureDescription": "You are currently restricted from creating new accounts."
}
A response that reports a service-side error:
{
"actionStatus": "ERROR",
"errorMessage": "External service failure",
"errorDescription": "The external service met with an unexpected error."
}
For the full API specification, see the flow extension API contract.
Next steps¶
Once your service is ready, configure the flow extension to register it as a connection and invoke it from a flow.
