Skip to content

Audit Logs

View as Markdown

Audit logs provide a detailed record of user and system activity across your organization. Each entry records an action, the actor and credential behind it, the resource it targeted, and the result.

Reading audit logs requires the audit read organization permission. Administrators hold it, as does the dedicated Auditor role, which grants read access to the log without the ability to turn logging off. Contact your organization administrator to get access to audit logs.

Audit logs are retained for 365 days.

Audit logs are used when an organization must know who did what, when, and with which credential.

  • Security audits: unauthorized access or sandbox misuse is traced to a user, an API key, an IP address, and a time. The key prefix identifies the key without exposing it.
  • Debugging: sandbox lifecycle problems, such as a failed start, are traced to the request that caused them. Each entry carries the HTTP status code and the error message.
  • Compliance reviews: actions across the organization stay on record for 365 days. Management-plane actions, such as changes to members and credentials, are always recorded and cannot be turned off.
  • Independent review: the Auditor role reads the log without the ability to turn logging off. Turning logging off is itself recorded as an update on the organization.
  • Access reviews: member role changes and removals, invitations, and the creation and role updates of service accounts are recorded with the previous and the new roles. A review shows who granted which access and when.
  • Credential changes: API key creation is recorded, along with the number of keys revoked when a project, a membership, or the organization is deleted. Keys minted for a service account carry the account ID.
  • Billing changes: wallet top-ups, plan changes, coupon redemptions, and billing contact changes are recorded as updates on the organization.
  • Live monitoring: new entries appear as they happen. Auto-refresh fetches the list every 10 seconds, 30 seconds, or 1 minute.
  • Scripted reviews: the CLI and the API filter entries by action, target, actor, status code, and time range. A script pages through the results to collect the entries for a review.
  1. Go to Daytona Dashboard ↗

Access the audit logs page directly from Daytona Dashboard ↗. The audit logs page displays a list of all audit logs for your organization, including the following columns:

ColumnDescription
TimeThe timestamp of the action.
ActorThe user or API key that performed the action.
ActionThe action performed.
TargetThe resource affected by the action.
OutcomesThe result of the action.

Select any row to open the full log entry, including its metadata.

To filter audit logs by time, use the date range picker in the top-left corner of the page. The filter menu next to it adds equality filters on action, target type, target ID, actor, and status code.

Refresh the audit log list as new events occur.

  1. Go to Daytona Audit Logs ↗
  2. Use the refresh control in the top-right corner of the page

The refresh icon fetches the current page immediately. The interval selector repeats that fetch every 10s, 30s, or 1m, or on Auto, which polls every 30 seconds. Auto-refresh is Off by default.

Each audit log entry contains the following fields:

FieldTypeDescription
idstringUnique log entry identifier
org_idstringID of the organization the action belongs to
project_idstringID of the project the action belongs to, or null for organization-level actions
actor_kindstringCredential type of the actor: user, api_key, org_key, vm, or system
actor_user_idstringID of the user who performed the action, or null for a machine credential
actor_emailstringEmail of the user who performed the action
actor_key_idstringID of the API key used, or null when the actor is a signed-in user
actor_key_prefixstringPrefix of the API key used, for identifying a key without exposing it
actionstringOperation executed (e.g. create, start, stop)
target_typestringResource type affected (e.g. sandbox, snapshot)
target_idstringID of the affected resource
status_codenumberHTTP status code of the result
error_messagestringError message if the action failed
ip_addressstringIP address of the request origin
user_agentstringUser agent of the request origin
metadataobjectAdditional context about the action
created_atstringRFC 3339 timestamp of when the action occurred

Get audit logs for a specific organization, newest first.

This endpoint accepts an organization-scoped API key carrying the audit read permission, or a signed-in dashboard session. A project-scoped API key is rejected.

Terminal window
daytona audit list --org {organizationId}

The response contains one page of entries and a cursor for the next page:

{
"next_cursor": "MjAyNi0wOS0wOVQxMDoxMjozMy4xMjM0NTY3ODlafDAxOTliMmMx...",
"audit_logs": [
{
"id": "{auditLogId}",
"org_id": "{organizationId}",
"project_id": "{projectId}",
"actor_kind": "user",
"actor_user_id": "{userId}",
"actor_email": "user@example.com",
"actor_key_id": null,
"actor_key_prefix": null,
"action": "create",
"target_type": "sandbox",
"target_id": "{sandboxId}",
"status_code": 200,
"error_message": null,
"ip_address": "203.0.113.42",
"user_agent": "daytona-sdk-python",
"metadata": { "class": "linux", "region": "{regionId}" },
"created_at": "2026-09-09T10:12:33.123456789Z"
}
]
}

All filters are optional and combine with AND. A filter written as field=value matches exactly. The API also accepts field[op]=value, where op is eq, not, in, not_in, gt, gte, lt, or lte. The in and not_in operators take a comma-separated list, and the range operators apply only to status. A request accepts at most 10 filters.

Terminal window
daytona audit list --org {organizationId} --limit 100 --action create --target-type sandbox
FlagTypeDescription
--limitintSets the number of entries per page. The default is 100.
--cursorstringPasses a next_cursor value.
--allboolFollows every page until the last one.
  • Each filter flag sets the matching query parameter.
  • For example, --target-id sets target_id.

List audit actions. The response lists every target type the audit log records and the actions possible for each, derived from the audited operations. Use it to build valid filters for audit log queries. The list is the same for every organization, and the request requires a credential.

from daytona import Daytona
daytona = Daytona()
actions = daytona.v2.list_audit_actions()
for target in actions.targets:
print(target.target_type, target.actions)

The response contains one entry per target type:

{
"targets": [
{
"target_type": "api_key",
"actions": ["create", "delete"]
},
{
"target_type": "checkpoint",
"actions": ["create"]
},
{
"target_type": "function",
"actions": ["create", "delete", "update"]
},
{
"target_type": "image",
"actions": ["create", "delete"]
},
{
"target_type": "org_api_key",
"actions": ["create", "delete"]
},
{
"target_type": "organization",
"actions": ["create", "delete", "update"]
},
{
"target_type": "organization_invitation",
"actions": ["create", "decline", "delete", "update"]
},
{
"target_type": "organization_user",
"actions": ["delete", "update"]
},
{
"target_type": "otel_config",
"actions": ["delete", "update"]
},
{
"target_type": "platform_token",
"actions": ["create"]
},
{
"target_type": "preview_endpoint",
"actions": ["create", "delete", "expire", "update"]
},
{
"target_type": "project",
"actions": ["create", "delete", "update"]
},
{
"target_type": "project_member",
"actions": ["create", "delete"]
},
{
"target_type": "project_service_account",
"actions": ["create", "delete"]
},
{
"target_type": "registry",
"actions": ["delete", "update"]
},
{
"target_type": "sandbox",
"actions": ["create", "delete", "fork", "pause", "resume", "start", "stop", "update"]
},
{
"target_type": "secret",
"actions": ["delete", "update"]
},
{
"target_type": "service_account",
"actions": ["create", "delete", "update"]
},
{
"target_type": "snapshot",
"actions": ["create", "delete", "replicate"]
},
{
"target_type": "volume",
"actions": ["create", "delete"]
}
]
}

Some pairs cover more than one operation:

  • organization update is recorded for changes to the organization itself, for the audit logging toggle, and for billing changes: wallet top-ups, the automatic top-up rule, coupon redemption, plan changes, the balance notification rule, and the billing contact email.
  • organization_invitation update is the invitee accepting the invitation. delete is the organization revoking it. decline is the invitee refusing it.
  • sandbox create is also recorded the first time a sandbox’s signing key is fetched, since that request mints the key. Later fetches of the same key are reads and are not recorded.
  • sandbox update covers label changes, lifecycle policy changes, and signing key rotation.
  • service_account delete archives the account and its keys; the record survives.

The metadata field carries context that the handler for the action already resolved. It never contains secret values. Entries for pairs not listed here have empty or null metadata.

Target typeActionKeys
sandboxcreateclass, region
sandboxforkforked_from: ID of the source sandbox
snapshotcreatekind: disk or full
imagecreateregion
api_keycreateservice_account_id when the key was minted for a service account. Otherwise no metadata
projectupdateprevious_name, name
projectdeleterevoked_keys: number of API keys revoked with the project
project_membercreateuser_ids on a bulk add, where target_id is the project ID. A single add has no metadata and target_id is the user ID
project_memberdeleterevoked_keys: number of API keys revoked with the membership
project_service_accountdeleterevoked_keys: number of API keys revoked with the membership
organizationupdateaudit_logs_disabled (boolean) when the entry records the audit logging toggle. Other updates have no metadata
organizationdeleterevoked_keys: number of API keys revoked, projects: number of projects deleted with the organization
organization_userupdateprevious_roles, roles, and dry_run: true when the change was only validated
organization_userdeleteprevious_roles, roles (empty), self_removal (boolean)
organization_invitationcreateemail, previous_roles, roles
organization_invitationdeleteemail, previous_roles, roles (empty)
organization_invitationdeclineprevious_roles, roles (empty)
service_accountcreateprevious_roles (empty), roles
service_accountupdateprevious_roles, roles, and dry_run: true when the change was only validated
service_accountdeletealready_archived (boolean)

The roles and previous roles fields are arrays of role slugs.

The outcome field indicates the result of the action. Statuses follow standard HTTP semantics:

OutcomeDescription
InfoInformational (1xx codes)
SuccessAction succeeded (2xx codes)
RedirectRedirects (3xx codes)
ErrorClient/server error (4xx/5xx)
UnknownNo status code was recorded on the entry

Audit logging is on by default. Organizations that do not want sandbox activity recorded can turn off capture on the workload plane. Management-plane actions, such as changes to the organization, its members, and its credentials, are always recorded and cannot be turned off. Neither can this setting itself: toggling it is recorded as an update on the organization target.

Turning capture off is not retroactive: earlier entries stay readable until retention ends.

Reading the setting requires audit read permission; changing it requires audit write permission, which no API key holds. This lets the Auditor role read the log but not disable it.

Terminal window
# Read the current setting
curl 'https://api.daytona.com/orgs/{organizationId}/audit-logs/config' \
--header 'Authorization: Bearer YOUR_OIDC_TOKEN'
# Turn workload-plane capture off
curl 'https://api.daytona.com/orgs/{organizationId}/audit-logs/config' \
--request PATCH \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
--data '{ "disabled": true }'
FieldTypeDescription
disabledbooleanTurns workload-plane capture off with true and on with false. This field is required.
  • The GET and PATCH endpoints require a signed-in dashboard session.
  • The GET and PATCH endpoints reject API keys.

Both return the current state:

{
"disabled": true
}