Actions Engine API
Actions are custom JavaScript code snippets that execute at specific points during authentication flows. They allow you to extend Auris with custom logic without modifying the core platform: enrich tokens with external data, block suspicious signups, enforce custom password policies, or sync user data to external systems.
Actions run in a sandboxed environment with restricted scope. Each action is associated with a trigger point (e.g., post-login) and executes in order of priority. Multiple actions can be attached to the same trigger.
All actions endpoints require the x-tenant header. Creating and managing actions requires admin-level access (implied by the admin:all permission or the admin:all permission).
Action Lifecycle
- Create an action with a trigger type and JavaScript code.
- Test the action by reviewing execution logs.
- Set the action’s status to
activeto enable it in production. - Monitor execution via the logs endpoint.
Action CRUD
List Actions
/api/actionsRequires: admin:allList all actions for the tenant. Returns action metadata including trigger type, status,
execution statistics, and ordering. Actions execute in ascending order of the order field
for each trigger type.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Items per page (default: 20) |
trigger | string | Filter by trigger type (e.g., post-login) |
status | active | inactive | Filter by status |
Success response
{
"ok": true,
"data": {
"data": [
{
"id": "act_abc123",
"name": "Enrich Token with CRM Data",
"trigger": "post-login",
"status": "active",
"order": 1,
"timeout": 5000,
"executionCount": 14523,
"errorCount": 12,
"lastExecutedAt": "2025-02-18T09:50:00Z",
"createdAt": "2025-01-10T10:00:00Z",
"updatedAt": "2025-02-15T14:00:00Z"
},
{
"id": "act_def456",
"name": "Block Disposable Emails",
"trigger": "pre-register",
"status": "active",
"order": 1,
"timeout": 3000,
"executionCount": 892,
"errorCount": 0,
"lastExecutedAt": "2025-02-18T08:30:00Z",
"createdAt": "2025-01-20T10:00:00Z",
"updatedAt": "2025-01-20T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1
}
}
}Create Action
/api/actionsRequires: admin:allCreate a new action. The action is created with inactive status by default. Set it to
active via the toggle endpoint after testing. The JavaScript code is validated for blocked
patterns before storage.
Request body
{
"name": "Enrich Token with CRM Data",
"trigger": "post-login",
"code": "async function handler(context, api) {\n const response = await api.fetch('https://crm.example.com/api/user', {\n headers: { 'X-User-Email': context.user.email }\n });\n const crmData = await response.json();\n api.setCustomClaim('crm_id', crmData.id);\n api.setCustomClaim('account_tier', crmData.tier);\n}",
"order": 1,
"timeout": 5000
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable name |
trigger | string | Yes | Trigger point (see Trigger Types) |
code | string | Yes | JavaScript function body |
order | integer | No | Execution order within the trigger (default: 0, lower = first) |
timeout | integer | No | Maximum execution time in milliseconds (default: 5000, max: 10000) |
Success response
{
"ok": true,
"data": {
"id": "act_ghi789",
"name": "Enrich Token with CRM Data",
"trigger": "post-login",
"status": "inactive",
"code": "async function handler(context, api) { ... }",
"order": 1,
"timeout": 5000,
"executionCount": 0,
"errorCount": 0,
"createdAt": "2025-02-18T10:00:00Z"
}
}Error codes
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Missing required fields or invalid trigger type |
BLOCKED_PATTERN | 400 | Code contains a blocked pattern (see Sandbox Restrictions) |
CODE_TOO_LARGE | 400 | Code exceeds the maximum allowed size |
Get Action
/api/actions/[id]Requires: admin:allRetrieve a single action by ID, including its full code and execution statistics.
Success response
{
"ok": true,
"data": {
"id": "act_abc123",
"name": "Enrich Token with CRM Data",
"trigger": "post-login",
"status": "active",
"code": "async function handler(context, api) {\n const response = await api.fetch('https://crm.example.com/api/user', {\n headers: { 'X-User-Email': context.user.email }\n });\n const crmData = await response.json();\n api.setCustomClaim('crm_id', crmData.id);\n api.setCustomClaim('account_tier', crmData.tier);\n}",
"order": 1,
"timeout": 5000,
"executionCount": 14523,
"errorCount": 12,
"lastExecutedAt": "2025-02-18T09:50:00Z",
"createdAt": "2025-01-10T10:00:00Z",
"updatedAt": "2025-02-15T14:00:00Z"
}
}Update Action
/api/actions/[id]Requires: admin:allUpdate an action’s name, code, trigger, order, or timeout. All fields are optional — only provided fields are updated. Updated code is re-validated for blocked patterns.
Request body
{
"name": "Enrich Token with CRM Data v2",
"code": "async function handler(context, api) {\n // Updated logic\n const data = await api.fetch('https://crm.example.com/v2/user/' + context.user.id);\n const user = await data.json();\n api.setCustomClaim('crm_id', user.id);\n}",
"timeout": 8000
}Success response
{
"ok": true,
"data": {
"id": "act_abc123",
"name": "Enrich Token with CRM Data v2",
"trigger": "post-login",
"status": "active",
"code": "async function handler(context, api) { ... }",
"order": 1,
"timeout": 8000,
"updatedAt": "2025-02-18T11:00:00Z"
}
}Delete Action
/api/actions/[id]Requires: admin:allDelete an action permanently. The action is immediately removed from the execution pipeline. Execution logs for this action are retained.
Success response
{
"ok": true,
"data": { "deleted": true }
}Toggle Action Status
/api/actions/[id]Requires: admin:allToggle an action between active and inactive status. Only active actions are executed
during authentication flows.
Request body
{
"status": "active"
}Valid values: active, inactive.
Success response
{
"ok": true,
"data": {
"id": "act_abc123",
"status": "active",
"updatedAt": "2025-02-18T11:30:00Z"
}
}Execution Logs
/api/actions/[id]/logsRequires: admin:allRetrieve execution logs for a specific action. Each log entry records whether the execution succeeded or failed, the duration, and any error messages. Logs are ordered by timestamp descending.
Query parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
limit | integer | Items per page (default: 20) |
Success response
{
"ok": true,
"data": {
"data": [
{
"id": "log_abc123",
"actionId": "act_abc123",
"trigger": "post-login",
"status": "success",
"duration": 234,
"userId": "usr_xyz789",
"createdAt": "2025-02-18T09:50:00Z"
},
{
"id": "log_def456",
"actionId": "act_abc123",
"trigger": "post-login",
"status": "error",
"duration": 5001,
"error": "Action timed out after 5000ms",
"userId": "usr_abc123",
"createdAt": "2025-02-18T09:48:00Z"
},
{
"id": "log_ghi789",
"actionId": "act_abc123",
"trigger": "post-login",
"status": "error",
"duration": 112,
"error": "TypeError: Cannot read property 'email' of undefined",
"userId": "usr_def456",
"createdAt": "2025-02-18T09:45:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 14535,
"totalPages": 727
}
}
}Log status values: success, error.
Trigger Types
Actions can be attached to one of six trigger points in the authentication pipeline:
| Trigger | Fires When | Use Cases |
|---|---|---|
pre-login | Before Keycloak authentication is attempted | Block login from specific IPs or email domains, custom rate limiting |
post-login | After successful authentication, before tokens are issued | Enrich tokens with external data, log custom analytics, sync to CRM |
pre-register | Before a new user account is created | Block disposable emails, enforce custom validation, check external blocklists |
post-register | After a new user account is created | Send welcome notification, create records in external systems, assign default roles |
post-change-password | After a user changes their password | Invalidate cached credentials, notify external systems, audit log |
pre-token | Before an M2M token is issued | Validate client scopes, add custom claims, enforce time-of-day restrictions |
Execution Order
When multiple active actions share the same trigger, they execute sequentially in ascending order value. If an action fails (throws an error or times out), subsequent actions for that trigger still execute unless the failing action explicitly denies the request.
ActionContext Object
Every action receives a context object as its first argument. The shape varies by trigger type.
pre-login / post-login
{
user: {
id: "usr_abc123",
email: "[email protected]",
username: "alice",
firstName: "Alice",
lastName: "Smith",
roles: ["editor", "viewer"],
emailVerified: true,
phoneNumber: "+39021234567",
phoneNumberVerified: true,
metadata: {}
},
connection: {
method: "password", // "password" | "magic_link" | "social" | "sso"
provider: null, // social provider name (e.g., "google") or null
ipAddress: "203.0.113.50",
userAgent: "Mozilla/5.0 ...",
timestamp: "2025-02-18T10:00:00Z"
},
tenant: "acme-corp"
}For pre-login, the user object may be null if the user has not been resolved yet (e.g., wrong email). The connection.method and connection.ipAddress are always available.
pre-register / post-register
{
user: {
email: "[email protected]",
username: "newuser",
firstName: "New",
lastName: "User"
},
connection: {
method: "password",
ipAddress: "203.0.113.50",
userAgent: "Mozilla/5.0 ...",
timestamp: "2025-02-18T10:00:00Z"
},
tenant: "acme-corp"
}For post-register, the user object also includes id and roles.
post-change-password
{
user: {
id: "usr_abc123",
email: "[email protected]"
},
tenant: "acme-corp"
}pre-token
{
application: {
id: "app_xyz789",
name: "Backend Service",
clientId: "m2m-client-id",
type: "M2M"
},
requestedScopes: ["read:users", "manage:roles"],
tenant: "acme-corp"
}ActionResult (API Object)
The second argument passed to actions is the api object, which provides methods to influence the authentication flow:
| Method | Available In | Description |
|---|---|---|
api.setCustomClaim(key, value) | post-login, pre-token | Add a custom claim to the access token |
api.setMetadata(key, value) | post-login, post-register | Set user metadata (persisted in the database) |
api.deny(reason) | pre-login, pre-register, pre-token | Deny the authentication attempt with a reason |
api.log(message) | All triggers | Write a message to the action’s execution log |
api.fetch(url, options) | All triggers | Make an HTTP request (restricted fetch with timeout) |
Example: Deny Signup for Disposable Emails
async function handler(context, api) {
const disposableDomains = ['tempmail.com', 'throwaway.email', 'guerrillamail.com'];
const domain = context.user.email.split('@')[1];
if (disposableDomains.includes(domain)) {
api.deny('Disposable email addresses are not allowed');
return;
}
api.log('Signup allowed for domain: ' + domain);
}Example: Enrich Token After Login
async function handler(context, api) {
try {
const response = await api.fetch('https://crm.example.com/api/lookup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer crm-api-key'
},
body: JSON.stringify({ email: context.user.email })
});
if (response.ok) {
const data = await response.json();
api.setCustomClaim('crm_id', data.customerId);
api.setCustomClaim('plan', data.subscriptionPlan);
api.log('Enriched token: plan=' + data.subscriptionPlan);
} else {
api.log('CRM lookup failed: ' + response.status);
}
} catch (error) {
api.log('CRM lookup error: ' + error.message);
// Do not deny login if enrichment fails
}
}Example: Block M2M Token Outside Business Hours
async function handler(context, api) {
var hour = new Date().getUTCHours();
if (hour < 6 || hour > 22) {
api.deny('M2M tokens cannot be issued outside business hours (06:00-22:00 UTC)');
return;
}
api.log('M2M token issued for ' + context.application.name + ' at UTC hour ' + hour);
}Sandbox Restrictions
Actions execute in a sandboxed environment with a restricted scope. The following patterns are detected and blocked at code validation time (during create and update). Code containing any of these patterns is rejected with a BLOCKED_PATTERN error:
require(— no CommonJS module importsimport— no ES module importsprocess.— no access to Node.js process objectchild_process— no shell executionfs./fs/promises— no filesystem accessglobal./globalThis.— no access to global scope- Dynamic code evaluation patterns — no runtime code generation from strings
The api.fetch() method is provided as a safe alternative to external HTTP libraries. It supports GET, POST, PUT, PATCH, and DELETE methods with JSON or text bodies. The timeout is inherited from the action’s timeout setting.
Runtime Limits
| Limit | Value |
|---|---|
| Maximum execution time | Configurable per action (default 5000ms, max 10000ms) |
| Maximum code size | 64 KB |
Maximum api.fetch() response size | 1 MB |
| Available globals | JSON, Date, Math, String, Number, Array, Object, Map, Set, Promise, RegExp, console.log (redirected to api.log) |
Error Handling
When an action throws an unhandled error or times out:
- The error is logged to the action’s execution log.
- The
errorCounton the action is incremented. - The authentication flow continues (actions do not block auth by default unless
api.deny()is called). - If the action is critical, use
api.deny()explicitly in your error handler.
Action errors do not block authentication by default. If you need a failing action to prevent login (e.g., a compliance check), you must call api.deny() explicitly in your catch block. Otherwise, the user will be authenticated even if the action fails.
Blueprint Visual Editor
The Auris Console includes a visual node editor (Blueprint Editor) for creating actions using a drag-and-drop interface instead of writing JavaScript. The Blueprint Editor generates JSON rule definitions that are compiled to equivalent JavaScript at runtime.
The Blueprint Editor is an alternative to the code editor — both produce the same result. Actions created with the Blueprint Editor can be viewed and edited as code, and vice versa.
See the Console documentation for details on the Blueprint Editor.
Related
- Actions & Sandboxed Execution — How the sandboxed execution engine works
- Writing Custom Actions — Step-by-step guide to creating actions
- Actions Engine — Create and manage actions from the Console
- Webhooks API — External event delivery that complements actions