Skip to main content

Audit Logging

Track what was performed, who did it, and the results


Clarifai's Audit Logging feature helps you monitor platform activities for better visibility, security, and governance. It captures detailed logs of operations so you can know what was done, who did it, and the results.

With Audit Logging, you can assess the actions performed on users and their resources, track resource changes to identify potential security issues, and maintain comprehensive activity logs to meet regulatory requirements.

tip

To learn how to perform audit tracking via the UI, see Teams & Logs within the Control Center.

info

The initialization code used in the following examples is outlined in detail on the client installation page.

List Audit Log Events

Here is how you can list all the supported audit log events performed by a user on the Clarifai platform.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject # The userDataObject is created in the overview and is required when using a PAT
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)
Output Example
{
"status": {
"code": 10000,
"description": "Ok",
"req_id": "c0c3788f56734a07ac468cffc9f96b82"
},
"entries": [
{
"timestamp": "2025-01-17T12:04:55.449860Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 403,
"description": "Workflow versions created",
"targets": [
{
"workflow": {
"id": "workflow-838250",
"use_cases": [],
"check_consents": []
}
},
{
"workflow_version": {
"id": "a72a9f65cb2648888b724291a0ba8cef"
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create workflow version 'a72a9f65cb2648888b724291a0ba8cef' for workflow 'workflow-838250'"
],
"success": true,
"req_id": "webportal-74e9a38774c449a598b67b369ae0064a",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T12:04:55.430103Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 400,
"description": "Workflows created",
"targets": [
{
"workflow": {
"id": "workflow-838250",
"use_cases": [],
"check_consents": []
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create workflow 'workflow-838250'"
],
"success": true,
"req_id": "webportal-74e9a38774c449a598b67b369ae0064a",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T12:00:06.599812Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 300,
"description": "Models created",
"targets": [
{
"model": {
"id": "visual-classifier-one2",
"app_id": "",
"toolkits": [],
"use_cases": [],
"languages": [],
"languages_full": [],
"check_consents": []
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create model 'visual-classifier-one2'"
],
"success": true,
"req_id": "webportal-7f53926068b3479bbc5631109ac64377",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T11:54:44.706982Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 300,
"description": "Models created",
"targets": [
{
"model": {
"id": "dogs-vs-cats",
"app_id": "",
"toolkits": [],
"use_cases": [],
"languages": [],
"languages_full": [],
"check_consents": []
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create model 'dogs-vs-cats'"
],
"success": true,
"req_id": "webportal-f488464806a042698840233f070adeec",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T11:51:52.065958Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 403,
"description": "Workflow versions created",
"targets": [
{
"app": {
"id": "test-app2"
}
},
{
"workflow": {
"id": "Universal",
"use_cases": [],
"check_consents": []
}
},
{
"workflow_version": {
"id": "12668c6aa7d149979d01ae716967a6c9"
}
}
],
"details": [
"Create workflow version '12668c6aa7d149979d01ae716967a6c9' for workflow 'Universal'"
],
"success": true,
"req_id": "webportal-9ca7bd88f14f412189312ca4e86dd965",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T11:51:52.056626Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 400,
"description": "Workflows created",
"targets": [
{
"workflow": {
"id": "Universal",
"use_cases": [],
"check_consents": []
}
},
{
"app": {
"id": "test-app2"
}
}
],
"details": [
"Create workflow 'Universal'"
],
"success": true,
"req_id": "webportal-9ca7bd88f14f412189312ca4e86dd965",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-17T11:51:52.014376Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 600,
"description": "Applications created",
"targets": [
{
"app": {
"id": "test-app2"
}
}
],
"details": [
"Create application 'test-app2'"
],
"success": true,
"req_id": "webportal-9ca7bd88f14f412189312ca4e86dd965",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-15T07:01:35.378946Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 403,
"description": "Workflow versions created",
"targets": [
{
"workflow": {
"id": "Universal",
"use_cases": [],
"check_consents": []
}
},
{
"workflow_version": {
"id": "eecdf9bb1e8b4394b9a6337814c4eb5d"
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create workflow version 'eecdf9bb1e8b4394b9a6337814c4eb5d' for workflow 'Universal'"
],
"success": true,
"req_id": "webportal-0c244223c21b4ee18800c1741ca7be5a",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-15T07:01:35.366665Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 400,
"description": "Workflows created",
"targets": [
{
"workflow": {
"id": "Universal",
"use_cases": [],
"check_consents": []
}
},
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create workflow 'Universal'"
],
"success": true,
"req_id": "webportal-0c244223c21b4ee18800c1741ca7be5a",
"source_ip": "3.234.120.41"
},
{
"timestamp": "2025-01-15T07:01:35.105312Z",
"user": {
"id": "alfrick",
"first_name": "Alfrick",
"last_name": "Opidi",
"company_name": "Clarifai",
"job_title": "Dev",
"job_role": "Data Science / Machine Learning",
"intention": "Train a custom model",
"created_at": "2024-01-11T05:36:46.977773Z",
"visibility": {
"gettable": 10
}
},
"operation": 600,
"description": "Applications created",
"targets": [
{
"app": {
"id": "testapp1"
}
}
],
"details": [
"Create application 'testapp1'"
],
"success": true,
"req_id": "webportal-0c244223c21b4ee18800c1741ca7be5a",
"source_ip": "3.234.120.41"
}
]
}

Filter Searches

You can optionally refine your searches to retrieve only the operations of interest. This enables targeted audit trails for the activities performed on the Clarifai platform.

Operation-Based Filtering

You can apply filters to target specific operation types.

The Audit Logging feature currently supports tracking the following critical resource operations (we're planning to support more resources in the future):

  • Organization and team membership activities — Includes creating, updating, or deleting organizations and teams, sending invitations, and managing team users and applications.
  • Module activities — Tracks the creation, updating, and deletion of modules.
  • Model activities — Tracks actions such as creating, training, publishing, and deleting models.
  • Workflow activities — Covers the creation, publishing, updating, and deletion of workflows.
  • Application activities — Includes creating, updating, duplicating, and deleting applications.
  • Collaborator activities — Includes adding collaborators, editing their scopes, and removing them.
Operations Event Types Supported
Event TypeCodeDescription
EVENT_TYPE_NOT_SET0Event type is not specified, lists all the supported audit log events
#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
query=resources_pb2.AuditLogQuery(
operations=[resources_pb2.EventType.MODEL_CREATE, resources_pb2.EventType.WORKFLOW_CREATE, resources_pb2.EventType.APPLICATION_CREATE]
)
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Time-Based Filtering

You can specify precise time ranges for your queries to track exactly when operations were performed.

Note that for HTTP+JSON requests, timestamps are formatted as 2024-08-25T00:00:00Z. And for gRPC requests, timestamps use separate seconds and nanos fields, as detailed here: Protobuf Timestamp Reference.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2
from google.protobuf.timestamp_pb2 import Timestamp
from datetime import datetime

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

# Helper function to convert string to Timestamp
def string_to_timestamp(date_string):
dt = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")
ts = Timestamp()
ts.FromDatetime(dt)
return ts

# Convert timestamps
timestamp_from = string_to_timestamp("2024-05-01T00:00:00Z")
timestamp_to = string_to_timestamp("2024-05-31T23:59:59Z")

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject,
query=resources_pb2.AuditLogQuery(
timestamp_from=timestamp_from,
timestamp_to=timestamp_to
)
),
metadata=metadata
)

if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

User-Based Filtering

You can define specific users or groups in your queries to track who performed each operation.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
query=resources_pb2.AuditLogQuery(
user_ids=[USER_ID]
)
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Target-Based Filtering

You can define the target of your query; that is, specify the resource on which an operation recorded in the audit log was performed.

Target Types Supported
Target
User user
Role role
Team team
App app
Module module
ModuleVersion module_version
Workflow workflow
WorkflowVersion workflow_version
Model model
ModelVersion model_version
#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
query=resources_pb2.AuditLogQuery(
targets=[
resources_pb2.AuditLogTarget(
user=resources_pb2.User(
id=USER_ID
)
)
]

)
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Success-Based Filtering

You can filter logs based on the operation outcome, such as whether it was successful. This also enables you to identify failed attempts and take appropriate action.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2
from google.protobuf.wrappers_pb2 import BoolValue

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
query=resources_pb2.AuditLogQuery(
success=BoolValue(value=True)
)
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Source-Based Filtering

You can filter logs based on the IP address where the request originated from.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
query=resources_pb2.AuditLogQuery(
source_ips=['127.0.0.0', '127.0.0.1']
)
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Sorting Logs

You can specify the sorting options for the audit logs.

  • If true, logs are sorted by timestamp in ascending order (oldest to newest).
  • If false, logs are sorted in descending order (newest to oldest).
#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
sort_ascending=True
),
metadata=metadata
)
if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)

Pagination

You can split the results into pages, which makes it easier to navigate and review the data.

#########################################################################
# In this section, we set the user authentication and user ID.
# Change these strings to run your own example.
#########################################################################

# Your PAT (Personal Access Token) can be found in the Account's Security section
PAT = 'YOUR_PAT_HERE'
USER_ID = 'YOUR_USER_ID_HERE'

###########################################################################
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
###########################################################################

from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
from clarifai_grpc.grpc.api.status import status_code_pb2
from google.protobuf.timestamp_pb2 import Timestamp
from datetime import datetime

channel = ClarifaiChannel.get_grpc_channel()
stub = service_pb2_grpc.V2Stub(channel)

metadata = (('authorization', 'Key ' + PAT),)

userDataObject = resources_pb2.UserAppIDSet(user_id=USER_ID)

# Helper function to convert string to Timestamp
def string_to_timestamp(date_string):
dt = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")
ts = Timestamp()
ts.FromDatetime(dt)
return ts

# Convert timestamps
timestamp_from = string_to_timestamp("2024-05-01T00:00:00Z")
timestamp_to = string_to_timestamp("2024-05-31T23:59:59Z")

post_audit_log_searches = stub.PostAuditLogSearches(
service_pb2.PostAuditLogSearchesRequest(
user_app_id=userDataObject,
query=resources_pb2.AuditLogQuery(
timestamp_from=timestamp_from,
timestamp_to=timestamp_to
),
pagination=service_pb2.Pagination(
page=2,
per_page=20
)
),
metadata=metadata
)

if post_audit_log_searches.status.code != status_code_pb2.SUCCESS:
print(post_audit_log_searches.status)
raise Exception("Post audit log searches failed, status: " + post_audit_log_searches.status.description)

print(post_audit_log_searches)