Audit Logs
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.
Use cases
Section titled “Use cases”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.
Access from Dashboard
Section titled “Access from Dashboard”- 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:
| Column | Description |
|---|---|
| Time | The timestamp of the action. |
| Actor | The user or API key that performed the action. |
| Action | The action performed. |
| Target | The resource affected by the action. |
| Outcomes | The 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.
Real-time updates
Section titled “Real-time updates”Refresh the audit log list as new events occur.
- Go to Daytona Audit Logs ↗
- 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.
Log structure
Section titled “Log structure”Each audit log entry contains the following fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique log entry identifier |
org_id | string | ID of the organization the action belongs to |
project_id | string | ID of the project the action belongs to, or null for organization-level actions |
actor_kind | string | Credential type of the actor: user, api_key, org_key, vm, or system |
actor_user_id | string | ID of the user who performed the action, or null for a machine credential |
actor_email | string | Email of the user who performed the action |
actor_key_id | string | ID of the API key used, or null when the actor is a signed-in user |
actor_key_prefix | string | Prefix of the API key used, for identifying a key without exposing it |
action | string | Operation executed (e.g. create, start, stop) |
target_type | string | Resource type affected (e.g. sandbox, snapshot) |
target_id | string | ID of the affected resource |
status_code | number | HTTP status code of the result |
error_message | string | Error message if the action failed |
ip_address | string | IP address of the request origin |
user_agent | string | User agent of the request origin |
metadata | object | Additional context about the action |
created_at | string | RFC 3339 timestamp of when the action occurred |
Get audit logs for organization
Section titled “Get audit logs for organization”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.
daytona audit list --org {organizationId}curl 'https://api.daytona.com/orgs/{organizationId}/audit-logs' \ --header 'Authorization: Bearer YOUR_API_KEY'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" } ]}Query parameters
Section titled “Query parameters”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.
daytona audit list --org {organizationId} --limit 100 --action create --target-type sandbox| Flag | Type | Description |
|---|---|---|
--limit | int | Sets the number of entries per page. The default is 100. |
--cursor | string | Passes a next_cursor value. |
--all | bool | Follows every page until the last one. |
- Each filter flag sets the matching query parameter.
- For example,
--target-idsetstarget_id.
curl 'https://api.daytona.com/orgs/{organizationId}/audit-logs?limit=100&action=create&target_type=sandbox' \ --header 'Authorization: Bearer YOUR_API_KEY'| Field | Type | Description |
|---|---|---|
limit | integer | Sets the number of entries per page. The maximum is 200, and larger values are clamped. The default is 100. |
cursor | string | Passes an opaque continuation token. Pass the next_cursor of the previous page. Pass back only cursors that the API returned. |
action | string | Returns only entries with this action. |
target_type | string | Returns only entries with this target type. |
target_id | string | Returns only entries affecting this resource. |
actor | string | Returns only entries with this user ID or API key ID. The value matches either actor_user_id or actor_key_id. A value that is not a UUID returns 400. |
status | integer | Returns only entries with this HTTP status code. |
start_at | string | Returns only entries recorded at or after this RFC 3339 timestamp. The bound is inclusive. |
end_at | string | Returns only entries recorded before this RFC 3339 timestamp. The bound is exclusive. |
List audit actions
Section titled “List audit actions”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)import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()const actions = await daytona.v2.listAuditActions()
for (const target of actions.targets) { console.log(target.target_type, target.actions)}curl 'https://api.daytona.com/audit/actions' \ --header 'Authorization: Bearer YOUR_API_KEY'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:
organizationupdateis 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_invitationupdateis the invitee accepting the invitation.deleteis the organization revoking it.declineis the invitee refusing it.sandboxcreateis 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.sandboxupdatecovers label changes, lifecycle policy changes, and signing key rotation.service_accountdeletearchives the account and its keys; the record survives.
Metadata
Section titled “Metadata”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 type | Action | Keys |
|---|---|---|
sandbox | create | class, region |
sandbox | fork | forked_from: ID of the source sandbox |
snapshot | create | kind: disk or full |
image | create | region |
api_key | create | service_account_id when the key was minted for a service account. Otherwise no metadata |
project | update | previous_name, name |
project | delete | revoked_keys: number of API keys revoked with the project |
project_member | create | user_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_member | delete | revoked_keys: number of API keys revoked with the membership |
project_service_account | delete | revoked_keys: number of API keys revoked with the membership |
organization | update | audit_logs_disabled (boolean) when the entry records the audit logging toggle. Other updates have no metadata |
organization | delete | revoked_keys: number of API keys revoked, projects: number of projects deleted with the organization |
organization_user | update | previous_roles, roles, and dry_run: true when the change was only validated |
organization_user | delete | previous_roles, roles (empty), self_removal (boolean) |
organization_invitation | create | email, previous_roles, roles |
organization_invitation | delete | email, previous_roles, roles (empty) |
organization_invitation | decline | previous_roles, roles (empty) |
service_account | create | previous_roles (empty), roles |
service_account | update | previous_roles, roles, and dry_run: true when the change was only validated |
service_account | delete | already_archived (boolean) |
The roles and previous roles fields are arrays of role slugs.
Outcomes
Section titled “Outcomes”The outcome field indicates the result of the action. Statuses follow standard HTTP semantics:
| Outcome | Description |
|---|---|
| Info | Informational (1xx codes) |
| Success | Action succeeded (2xx codes) |
| Redirect | Redirects (3xx codes) |
| Error | Client/server error (4xx/5xx) |
| Unknown | No status code was recorded on the entry |
Turn audit logging off
Section titled “Turn audit logging off”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.
# Read the current settingcurl 'https://api.daytona.com/orgs/{organizationId}/audit-logs/config' \ --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
# Turn workload-plane capture offcurl '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 }'| Field | Type | Description |
|---|---|---|
disabled | boolean | Turns workload-plane capture off with true and on with false. This field is required. |
- The
GETandPATCHendpoints require a signed-in dashboard session. - The
GETandPATCHendpoints reject API keys.
Both return the current state:
{ "disabled": true}