Activity logs
The activity command (aliases: activity-logs, logs) mirrors the
/activity-logs page in the app. It returns the customer-journey
activity feed — every action you and your team have taken across the
account, with the same filters the page exposes plus a client-side
substring search for the page's search box.
Backed by GET /api/v1/app/activity-logs.
Owner-only. The backend gates this endpoint to the account owner. A team member's
oc_live_token gets a 403 — same as visiting the page in the app.
Quick start
splashify activity # latest 100 entries
splashify activity --limit 500 # more
splashify activity --action login # filter by action
splashify activity --entity contact # filter by entity type
splashify activity --search "alice" # text search across actor/descriptionCommand reference
splashify activity is a single command with flags. There is no
sub-tree (no per-entry GET, no write actions — logs are append-only and
written by the backend).
splashify activity [--limit N] \
[--action <action>] \
[--entity <entity_type>] \
[--entity-id <id>] \
[--actor <actor_user_id>] \
[--search "<text>"]Known values
Use these filter values for predictable results. Custom actions and entity types are written occasionally (per-feature handlers) and are still returned without a filter.
Actions:
Entity types:
Response shape
{
"success": true,
"logs": [
{
"log_id": "uuid",
"action": "created",
"entity_type": "contact",
"entity_id": "uuid",
"entity_name": "Alice Smith",
"actor_id": "uuid",
"actor_name": "You",
"actor_email": "[email protected]",
"actor_role": "owner",
"description": "You created contact Alice Smith",
"metadata": "{}",
"created_at": "2026-05-20T14:00:00Z"
}
]
}When --search is set, the CLI emits the filtered shape:
{
"success": true,
"logs": [/* matched entries */],
"count": 3
}Common workflows
Who deleted what today?
TODAY=$(date -u '+%Y-%m-%d')
splashify activity --action deleted --limit 500 | \
jq --arg t "$TODAY" '.logs[] | select(.created_at | startswith($t)) |
{time: .created_at, actor: .actor_name, entity: .entity_type, name: .entity_name}'Audit logins for a specific person
ACTOR=$(splashify member --search "[email protected]" | jq -r '.member_id')
splashify activity --action login --actor "$ACTOR" --limit 100Track every change to one contact
splashify activity --entity contact --entity-id <contact_id> --limit 200 | \
jq '.logs[] | {time: .created_at, actor: .actor_name, action, desc: .description}'Quick "who's been busy" report
splashify activity --limit 500 | \
jq '.logs | group_by(.actor_name) | map({actor: .[0].actor_name, count: length}) | sort_by(-.count)'Filter for a specific keyword in the description
# Server filters can't search text — that's what --search is for
splashify activity --search "broadcast" --limit 500Export to JSONL for offline analysis
splashify activity --limit 5000 | \
jq -c '.logs[]' > activity.jsonl
wc -l activity.jsonlTroubleshooting
Got HTTP 403 — the calling token is bound to a non-owner team member. Only owners can read activity logs. Switch to an owner token (or run the command from the owner's machine).
--search returns nothing even though I see results in the raw list —
the search runs after the server-side filters. If --limit is
small (e.g. 100), the matching entry might be beyond it. Raise
--limit to widen the window.
Empty metadata field — most actions write {}. Specific actions
(e.g. broadcast cancels, role changes) include extra context here as a
JSON-encoded string. Parse with jq -r '.logs[0].metadata | fromjson'.
Logs from a custom action don't show up — the page indexes a fixed
set of known action values for the dropdown, but the backend stores
whatever the handler wrote. Use the --action <custom> flag exactly
matching the handler's value.
See also
splashify team— to look upactor_idfor a team member you want to audit.splashify support— to file a ticket referencing the relevant log entries.splashify devices— to remotely revoke a session if you spot suspicious logins here.