Mastering Date Logic in Genesys Cloud Architect: DateTimeDiff and GetDayOfWeek

Mastering Date Logic in Genesys Cloud Architect: DateTimeDiff and GetDayOfWeek

What You Will Build

  • One sentence: You will build an Architect flow that dynamically calculates the age of a case and filters routing logic based on the current day of the week.
  • One sentence: This uses the Genesys Cloud Architect API to inspect and validate flow logic, specifically focusing on the expression language syntax for date manipulation.
  • One sentence: The programming languages covered are Python (for API verification) and Genesys Cloud Architect Expression Language (for flow logic).

Prerequisites

  • OAuth client type: Client Credentials Flow.
  • Required scopes: flow:flow:read, flow:flow:write, analytics:events:read.
  • SDK version: Genesys Cloud Python SDK genesyscloud v5.0+.
  • Language/runtime requirements: Python 3.9+.
  • External dependencies: pip install genesyscloud.

Authentication Setup

Before interacting with the Architect API to validate your date logic, you must establish an authenticated session. The Genesys Cloud Python SDK handles the OAuth token refresh automatically, but you must initialize the client correctly.

import os
from genesyscloud import PlatformClient
from genesyscloud.flows.client import create_flows_api_client

# Initialize the Platform Client
platform_client = PlatformClient()

# Configure the client with environment variables
# Do not hardcode credentials. Use environment variables or a secrets manager.
platform_client.set_auth(
    client_id=os.getenv("GENESYS_CLIENT_ID"),
    client_secret=os.getenv("GENESYS_CLIENT_SECRET"),
    base_url=os.getenv("GENESYS_BASE_URL", "https://api.mypurecloud.com")
)

# Create the Flows API client instance
flows_api = create_flows_api_client(platform_client)

def verify_authentication():
    """Verifies that the OAuth token is valid and scopes are correct."""
    try:
        # A simple read operation to test connectivity
        flows = flows_api.get_flows(page_size=1)
        if flows.entities:
            print("Authentication successful. Connected to Genesys Cloud.")
            return True
        else:
            print("No flows found, but authentication succeeded.")
            return True
    except Exception as e:
        print(f"Authentication failed: {e}")
        return False

if verify_authentication():
    print("Ready to inspect date logic.")

OAuth Scopes Required: flow:flow:read

Implementation

Step 1: Understanding the DateTimeDiff Syntax

The DateTimeDiff function is the primary tool for calculating the duration between two timestamps in Architect. It returns the difference in a specified unit (seconds, minutes, hours, days, weeks, months, or years).

The syntax is:

DateTimeDiff(timestamp1, timestamp2, unit)
  • timestamp1: The start time (usually the older time).
  • timestamp2: The end time (usually the current time or a future target).
  • unit: A string literal: "s", "m", "h", "d", "w", "mo", or "y".

Common Pitfall: The order of arguments matters. If timestamp1 is later than timestamp2, the result will be negative. Architect expressions evaluate strictly left-to-right.

Code Example: Calculating Case Age

Suppose you have a case object with a createdTimestamp property. You want to check if the case is older than 48 hours.

In an Architect Set Data or Filter node, the expression would look like this:

# This is the expression string you would place in the Architect UI
# or define in a flow JSON definition.

expression = "DateTimeDiff(case.createdTimestamp, now(), 'h') > 48"

To validate this logic via the API, you can inject this expression into a test flow definition. However, since the Architect API does not have a “sandbox” evaluator endpoint for arbitrary expressions, we must verify the syntax by attempting to create or update a flow that uses it.

Here is how you construct a minimal flow definition in Python to test the validity of this expression structure:

from genesyscloud.models import Flow, FlowConditions, FlowActions

def create_test_flow_with_datetime_diff(flow_name: str, expression: str):
    """
    Attempts to create a flow with the specified DateTimeDiff expression.
    This validates the syntax against the Genesys Cloud schema.
    """
    
    # Define the action that uses the expression
    # In a real flow, this would be a 'Filter' or 'Set Data' action.
    # Here we use a 'Log' action for simplicity to avoid side effects.
    
    action = FlowActions(
        type="log",
        settings={
            "level": "debug",
            "message": f"Case age check: {expression}"
        },
        conditions=[
            FlowConditions(
                type="expression",
                expression=expression
            )
        ]
    )

    # Define the flow structure
    flow_body = Flow(
        name=flow_name,
        type="outbound", # Using outbound as a simple base type
        version=1,
        actions=[action],
        initial_status="draft"
    )

    try:
        # Attempt to create the flow
        # This will raise an error if the expression syntax is invalid
        response = flows_api.post_flows(body=flow_body)
        print(f"Flow created successfully with ID: {response.id}")
        return response.id
    except Exception as e:
        print(f"Syntax validation failed: {e}")
        return None

# Test the expression
flow_id = create_test_flow_with_datetime_diff(
    "Test DateTimeDiff Syntax",
    "DateTimeDiff(case.createdTimestamp, now(), 'h') > 48"
)

Expected Response:
If the expression is syntactically correct, the API returns a 201 Created status with the flow JSON. If the unit is misspelled (e.g., "hours" instead of "h"), the API returns a 400 Bad Request with a detailed error message pointing to the invalid expression.

Error Handling:

except Exception as e:
    if "400" in str(e):
        print("Expression syntax error. Check the unit string and argument order.")
    elif "401" in str(e):
        print("Authentication token expired.")
    else:
        print(f"Unexpected error: {e}")

Step 2: Using GetDayOfWeek for Routing Logic

The GetDayOfWeek function returns an integer representing the day of the week for a given timestamp.

The syntax is:

GetDayOfWeek(timestamp)
  • timestamp: The date/time to evaluate. Commonly now() or a specific variable like case.dueDate.
  • Return Value: An integer from 0 to 6.
    • 0 = Sunday
    • 1 = Monday
    • 2 = Tuesday
    • 3 = Wednesday
    • 4 = Thursday
    • 5 = Friday
    • 6 = Saturday

Critical Note: This function is locale-independent. It always returns 0 for Sunday and 6 for Saturday, regardless of the user’s regional settings. This is a common source of bugs when developers assume Monday is 1 in all contexts (in some libraries, it is, but not in Genesys Architect).

Code Example: Filtering Weekdays

You want to route calls to a specialized team only if the current day is Monday through Friday (1-5).

The expression is:

expression = "GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"

To make this more readable and maintainable, you can store the day in a variable first.

Step 2a: Set Data Node

# Expression in a Set Data node to set a variable 'currentDay'
set_data_expression = "GetDayOfWeek(now())"

Step 2b: Filter Node

# Expression in a Filter node
filter_expression = "currentDay >= 1 AND currentDay <= 5"

Let us validate this combined logic via the API by creating a flow that sets the data and then filters.

def create_weekday_routing_flow(flow_name: str):
    """
    Creates a flow that sets the current day of week and filters for weekdays.
    """
    
    # Action 1: Set Data
    set_data_action = FlowActions(
        type="setdata",
        settings={
            "data": {
                "currentDay": "GetDayOfWeek(now())"
            }
        }
    )

    # Action 2: Filter (Weekdays Only)
    weekday_filter = FlowActions(
        type="filter",
        settings={
            "expression": "currentDay >= 1 AND currentDay <= 5"
        },
        next_actions=[
            {"id": "weekday_queue", "condition": "true"},
            {"id": "weekend_queue", "condition": "false"}
        ]
    )

    # Action 3: Queue (Weekday)
    weekday_queue_action = FlowActions(
        type="queue",
        settings={
            "queueId": "your-weekday-queue-id", # Replace with real ID
            "name": "Weekday Support"
        },
        id="weekday_queue"
    )

    # Action 4: Queue (Weekend)
    weekend_queue_action = FlowActions(
        type="queue",
        settings={
            "queueId": "your-weekend-queue-id", # Replace with real ID
            "name": "Weekend Support"
        },
        id="weekend_queue"
    )

    flow_body = Flow(
        name=flow_name,
        type="routing",
        version=1,
        actions=[set_data_action, weekday_filter, weekday_queue_action, weekend_queue_action],
        initial_status="draft"
    )

    try:
        response = flows_api.post_flows(body=flow_body)
        print(f"Weekday routing flow created: {response.id}")
        return response.id
    except Exception as e:
        print(f"Flow creation failed: {e}")
        return None

flow_id = create_weekday_routing_flow("Weekday Routing Test")

Expected Response:
A successful 201 Created response. If the queue IDs are invalid, the API may still accept the flow definition but will fail at runtime or during validation if you enable strict mode. For pure syntax validation, the expression itself is the focus.

Error Handling:
If you mistakenly use GetDayOfWeek("2023-10-01") without a time component, the API might interpret it as midnight UTC. Always ensure your timestamps are fully formed ISO 8601 strings or use now().

Step 3: Combining DateTimeDiff and GetDayOfWeek

Advanced routing often requires both time elapsed and day-of-week logic. For example, “If the case is older than 24 hours AND it is a weekday, escalate it.”

Expression:

expression = "DateTimeDiff(case.createdTimestamp, now(), 'h') > 24 AND GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"

This expression can be placed directly in a Filter node. It avoids the need for an intermediate Set Data node, reducing flow complexity.

Code Example: Complex Escalation Flow

def create_escalation_flow(flow_name: str, escalation_queue_id: str):
    """
    Creates a flow that escalates cases older than 24h on weekdays.
    """
    
    # Filter Action
    escalation_filter = FlowActions(
        type="filter",
        settings={
            "expression": "DateTimeDiff(case.createdTimestamp, now(), 'h') > 24 AND GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"
        },
        next_actions=[
            {"id": "escalate", "condition": "true"},
            {"id": "normal_route", "condition": "false"}
        ]
    )

    # Escalation Action
    escalate_action = FlowActions(
        type="queue",
        settings={
            "queueId": escalation_queue_id,
            "name": "Escalation Queue"
        },
        id="escalate"
    )

    # Normal Route Action (Placeholder)
    normal_route_action = FlowActions(
        type="queue",
        settings={
            "queueId": "normal-queue-id", # Replace with real ID
            "name": "Normal Queue"
        },
        id="normal_route"
    )

    flow_body = Flow(
        name=flow_name,
        type="routing",
        version=1,
        actions=[escalation_filter, escalate_action, normal_route_action],
        initial_status="draft"
    )

    try:
        response = flows_api.post_flows(body=flow_body)
        print(f"Escalation flow created: {response.id}")
        return response.id
    except Exception as e:
        print(f"Flow creation failed: {e}")
        return None

flow_id = create_escalation_flow("Case Escalation Logic", "escalation-queue-id")

Expected Response:
201 Created. The flow is now available in Architect. You can publish it to make it active.

Error Handling:
If the case.createdTimestamp is null, DateTimeDiff will return null. In Architect, comparisons with null result in false. This is safe behavior. However, if you want to explicitly handle nulls, you can add a check:

expression = "case.createdTimestamp != null AND DateTimeDiff(case.createdTimestamp, now(), 'h') > 24 AND GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"

Complete Working Example

Below is a complete Python script that validates both DateTimeDiff and GetDayOfWeek syntax by creating and then deleting test flows. This ensures no clutter is left in your Genesys Cloud environment.

import os
import sys
from genesyscloud import PlatformClient
from genesyscloud.flows.client import create_flows_api_client
from genesyscloud.models import Flow, FlowConditions, FlowActions

def main():
    # Initialize Client
    platform_client = PlatformClient()
    platform_client.set_auth(
        client_id=os.getenv("GENESYS_CLIENT_ID"),
        client_secret=os.getenv("GENESYS_CLIENT_SECRET"),
        base_url=os.getenv("GENESYS_BASE_URL", "https://api.mypurecloud.com")
    )
    flows_api = create_flows_api_client(platform_client)

    # Test Expressions
    test_expressions = [
        {
            "name": "DateTimeDiff Hours",
            "expr": "DateTimeDiff(case.createdTimestamp, now(), 'h') > 24"
        },
        {
            "name": "GetDayOfWeek Weekday",
            "expr": "GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"
        },
        {
            "name": "Combined Logic",
            "expr": "DateTimeDiff(case.createdTimestamp, now(), 'h') > 24 AND GetDayOfWeek(now()) >= 1 AND GetDayOfWeek(now()) <= 5"
        }
    ]

    created_flow_ids = []

    for test in test_expressions:
        print(f"Testing expression: {test['name']}")
        
        # Create a simple flow with the expression in a log action
        action = FlowActions(
            type="log",
            settings={
                "level": "debug",
                "message": f"Test: {test['expr']}"
            },
            conditions=[
                FlowConditions(
                    type="expression",
                    expression=test['expr']
                )
            ]
        )

        flow_body = Flow(
            name=f"Test_{test['name'].replace(' ', '_')}",
            type="outbound",
            version=1,
            actions=[action],
            initial_status="draft"
        )

        try:
            response = flows_api.post_flows(body=flow_body)
            print(f"  -> Syntax Valid. Flow ID: {response.id}")
            created_flow_ids.append(response.id)
        except Exception as e:
            print(f"  -> Syntax Invalid: {e}")

    # Cleanup: Delete created flows
    if created_flow_ids:
        print("\nCleaning up test flows...")
        for fid in created_flow_ids:
            try:
                flows_api.delete_flow(flow_id=fid)
                print(f"  -> Deleted flow {fid}")
            except Exception as e:
                print(f"  -> Failed to delete flow {fid}: {e}")

    print("\nValidation complete.")

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 400 Bad Request - “Invalid expression”

  • What causes it: The expression syntax is malformed. Common mistakes include:
    • Using "hours" instead of "h" in DateTimeDiff.
    • Using "Monday" instead of 1 in GetDayOfWeek.
    • Missing parentheses in complex logical expressions.
    • Using single quotes ' inside double quotes " without escaping.
  • How to fix it:
    • Check the unit string in DateTimeDiff. It must be one of: "s", "m", "h", "d", "w", "mo", "y".
    • Ensure GetDayOfWeek returns an integer, not a string.
    • Use the Python script above to isolate the expression. If the script fails, the expression is syntactically invalid.

Error: 401 Unauthorized

  • What causes it: The OAuth token has expired or the client credentials are incorrect.
  • How to fix it:
    • Verify GENESYS_CLIENT_ID and GENESYS_CLIENT_SECRET are correct.
    • Ensure the client has the flow:flow:write scope.
    • The SDK handles token refresh, but if the client was created long ago, re-initialize it.

Error: Runtime Error - “Null value”

  • What causes it: The timestamp variable (e.g., case.createdTimestamp) is null.
  • How to fix it:
    • Add a null check: case.createdTimestamp != null AND ...
    • Ensure the data source (e.g., CTI object, CRM connector) is populating the timestamp correctly.

Official References