Scriptlets
Flows are built from screens, actions, conditions, and connectors. Those steps are customizable, but their outputs are fixed shapes that Descope writes into the flow context.
When you need something more flexible — hashing, date math, string transforms, or other small logic that does not fit a built-in action — use the Scriptlet action. It runs custom JavaScript in the flow and writes the values you return back into context.

Using Scriptlets
Add a Scriptlet action from the flow editor, then configure arguments, code, the result context key, and optional overrides.
Arguments
Arguments pass values into the script. Each argument has a name (used as a variable in your code) and a source:
| Type | Description |
|---|---|
| Dynamic | Value read from flow context at runtime (for example form.displayName) |
| String / Boolean / Number / Time | Static value you set in the action, validated by type |

In the example above, the Scriptlet builds a greeting string. If form.displayName is empty, it falls back to a static default such as John.
Available Libraries
The Scriptlet runtime includes:
- Lodash — iteration, object helpers, and similar utilities
- CryptoJS — hashing and related crypto helpers
Use them directly in your script (for example Lodash collection helpers or CryptoJS hash functions).
Note
Secure random number generation (for example CryptoJS.lib.WordArray.random()) is not supported in Scriptlets.
For non-cryptographic random values, use Math.random() instead.
Context Key
The object your script returns is stored under the context key you configure (default paths look like scripts.scriptletResult).

Example output shape:
{
"scripts": {
"scriptletResult": {
"greeting": "Hello, John!"
}
}
}Later actions and conditions can read those keys from context (for example scripts.scriptletResult.greeting).
Overrides
Use Overrides to copy Scriptlet results onto existing flow context keys such as form.displayName, form.email, or form.customAttributes.*.

For example, strip + subaddresses from an email in the Scriptlet, return { email: result }, then override form.email with scripts.scriptletResult.email:
let result = email;
const domainIndex = email.lastIndexOf('@');
if (domainIndex > -1) {
// Treat '+' after the first character as a subaddress separator
const aliasIndex = email.substring(1).indexOf('+') + 1;
if (aliasIndex > 0) {
result = email.substring(0, aliasIndex) + email.substring(domainIndex);
}
}
return {
email: result,
};
If you override a custom screen input, define that field's context key on the screen component first.

Overriding the Tenant
To switch tenants in an existing session without running another flow, use selectTenant on the client SDKs.
If a Scriptlet overrides tenant-related context keys, Descope includes the dct (Descope Current Tenant) claim in the resulting JWT, updates JWT structure as needed, and applies tenant-specific roles and permissions.
Logging and Debugging
Scriptlets support the standard console methods - console.log(), console.warn(), console.error(), and console.debug().
Anything you log is written to the Flow Runner messages when you run the flow from the Descope console. This lets you inspect intermediate values while the flow executes, without having to return them into flow context just to see them.
console.log(`Incoming email: ${email}`);
const domainIndex = email.lastIndexOf('@');
if (domainIndex === -1) {
console.error(`Email is missing a domain: ${email}`);
return {};
}
const domain = email.substring(domainIndex + 1);
console.debug(`Extracted domain: ${domain}`);
return {
domain: domain,
};
To view the output, run your flow from Flows in the Descope console and check the Runner messages panel below your flows.
Testing Scriptlets
Use Test in the Scriptlet editor before you rely on the action in a live flow.
The test panel lists the Arguments you defined above. For each Dynamic argument, you can override the value that would normally come from flow context — enter any sample input you want to try. Static arguments keep the values you already set on the action.
Run the test to see the object the Scriptlet would return (and therefore what lands under your context key). That makes it easy to confirm hashing, date math, or string transforms without stepping through the full flow.

Examples
Here are some examples of how to use Scriptlets in Flows.
Hash an Email Domain for a Tenant ID
Create a tenant for a new user and derive a stable tenant ID from the user's email domain (for example with CryptoJS hashing), then pass that value into Create Tenant.



Time-Based MFA Condition
Prompt for MFA when the user has not authenticated in the last 30 days.

Compare last authentication time to the current time in a Scriptlet:

Use the boolean (or age) output in a Condition step:
