Portal Community

Node Configuration

{
  "nodeType": "UserLookup",
  "name": "fetchApprover",
  "config": {
    "lookupBy": "email",
    "value": "$output.fetchInvoice.managerEmail",
    "includeRoles": true,
    "failOnNotFound": true
  }
}

Configuration Fields

FieldValuesDescription
lookupByemail | employeeId | username | userIdThe identifier type to search by
valuestring / exprThe lookup value. Expressions evaluated against execution context.
includeRolesboolWhether to include the user's role array in the output. Default: false.
failOnNotFoundboolIf true, routes to error port when user not found. If false, outputs null. Default: true.

Node Output

{
  "userId": "usr-a1b2c3",
  "email": "alice@acme.com",
  "displayName": "Alice Kowalski",
  "username": "alice.kowalski",
  "employeeId": "EMP-0042",
  "isActive": true,
  "roles": ["approver", "finance-manager"],
  "managerId": "usr-mgr001",
  "department": "Finance"
}

Using Output in Downstream Nodes

// In an ApprovalNode actor field:
"actorId": "$output.fetchApprover.userId"

// In a SendSlackMessage channelId:
"channelId": "@$output.fetchApprover.username"

// In a condition expression:
"$output.fetchApprover.roles.includes('finance-manager')"

// In a ForEach items field to iterate over roles:
"items": "$output.fetchApprover.roles"

UserLookupExecutor

public class UserLookupExecutor : BaseNodeExecutor<UserLookupConfig>
{
    private readonly IPassportClient _passport;

    protected override async Task<NodeExecutionResult> ExecuteAsync(
        UserLookupConfig config,
        NodeDataContext ctx,
        CancellationToken ct)
    {
        var lookupValue = _evaluator.Evaluate<string>(config.Value, ctx);

        var request = new UserLookupRequest
        {
            LookupBy = config.LookupBy,
            Value = lookupValue,
            IncludeRoles = config.IncludeRoles,
            TenantId = ctx.TenantId
        };

        var user = await _passport.LookupUserAsync(request, ct);

        if (user == null)
        {
            if (config.FailOnNotFound)
                return NodeExecutionResult.Fail(new IdentityNotFoundException(
                    $"User not found by {config.LookupBy}: {lookupValue}"));
            return NodeExecutionResult.Success(null);
        }

        return NodeExecutionResult.Success(new
        {
            user.UserId,
            user.Email,
            user.DisplayName,
            user.Username,
            user.EmployeeId,
            user.IsActive,
            Roles = config.IncludeRoles ? user.Roles : null,
            user.ManagerId,
            user.Department
        });
    }
}
Caching: The IPassportClient implementation caches user lookups for 60 seconds within an execution to avoid repeated Passport API calls for the same user. If a workflow queries the same user in multiple nodes, only the first call hits the network.