Tested CLI workflows
Select the right context, supply bounded dates, and understand lifecycle requirements.
Use these commands with the host and project for your environment. For dev, use
https://dev.neatlogs.com; for the US customer app, use https://app.neatlogs.com;
for EU, use https://eu.app.neatlogs.com after public API deployment there.
Credentials issued by one region or environment cannot authorize another.
Select context before reading data
neatlogs profile set work --host 'https://app.neatlogs.com'
neatlogs profile use work
neatlogs auth login
neatlogs projects list --json
neatlogs profile set work --host 'https://app.neatlogs.com' \
--project '<project-uuid>'
neatlogs projects current --jsonprojects list is an organization-level operation: omit explicit --project and NEATLOGS_PROJECT_ID for that command. A project saved in a profile can still be used by project-level commands.
Analytics requires both dates
Every analytics query requires an inclusive --from and an exclusive --to, in RFC 3339 format. The query window must be positive and no longer than 30 days. Choose a window that contains the traces you want to inspect; an empty result can be valid.
FROM='2026-09-23T00:00:00Z'
TO='2026-09-30T00:00:00Z'
neatlogs analytics kpis --from "$FROM" --to "$TO" --json
neatlogs analytics cost --from "$FROM" --to "$TO" --json
neatlogs analytics chart --from "$FROM" --to "$TO" \
--granularity day --timezone UTC --json
neatlogs analytics facets --from "$FROM" --to "$TO" \
--field workflowName --limit 20 --jsonneatlogs analytics kpis --json alone is incomplete. An argument error is local validation and has no server request ID because it does not send an HTTP request.
Read traces and continue pagination
neatlogs traces list --from "$FROM" --to "$TO" --limit 5 --json
neatlogs traces list --from "$FROM" --to "$TO" \
--limit 5 --cursor '<page.nextCursor>' --json
neatlogs traces get '<trace-id>' --json
neatlogs traces spans list '<trace-id>' --limit 25 --json
neatlogs traces export '<trace-id>' --format json --jsonUse the same filters and dates when continuing a cursor. A cursor is bound to its operation, actor and project; it cannot be moved between projects or environments. --all --max-items 100 gives collection an explicit ceiling.
Keyword search is a separate read path. Successful trace listing does not establish that search's index is ready. A 503 from search requires investigation of the deployment's search readiness; do not treat it as an empty search result.
Comment and evaluation prerequisites
comments create adds a message to an existing thread:
neatlogs comments list --trace '<trace-id>' --limit 25 --json
printf '%s' 'Review note' | neatlogs comments create \
--trace '<trace-id>' --thread '<thread-id>' --content-stdin --jsonThe Public API does not create the initial trace-comment thread. If the trace has no thread, create it in the dashboard first. Comment writes require an OAuth user with observability:write and the corresponding current role permission.
A new evaluation is a draft. The current Public API launch workflow assigns the signed-in user with assignMe: true; it does not accept a list of other reviewers. See evaluation commands for the cohort, form and launch requirements. Do not use draft creation as proof that review dispatch or completion works. Draft deletion is not available through this API.
Complete a self-review evaluation
Use a trace from the selected project and a current role that allows evaluation writes. Request write scopes explicitly when signing in with a client that defaults to read scopes:
neatlogs auth login --scope context:read observability:read \
evaluation:read evaluation:write offline_access
TRACE_ID='<trace-id>'
EVAL_ID=$(printf '%s' '{"name":"CLI self-review","type":"human","traceSource":"existing","sendFrequency":"manual"}' | \
neatlogs evals create --body-stdin --json | jq -r '.data.id')
printf '%s' '{"name":"Review","questions":[{"answerType":"short_answer","questionText":"Review result","isRequired":true,"sortOrder":0}]}' | \
neatlogs evals form set "$EVAL_ID" --body-stdin --json
PREVIEW_HASH=$(jq -n --arg trace "$TRACE_ID" \
'{action:"add",references:[{kind:"trace",traceId:$trace}]}' | \
neatlogs evals cohort preview "$EVAL_ID" --request-stdin --json | \
jq -r '.data.normalizedInputHash')
jq -n --arg trace "$TRACE_ID" --arg hash "$PREVIEW_HASH" \
'{previewHash:$hash,references:[{kind:"trace",traceId:$trace}]}' | \
neatlogs evals cohort add "$EVAL_ID" --request-stdin --json
neatlogs evals launch "$EVAL_ID" --assign-me --review-duration 1h --json
ITEM_ID=$(neatlogs evals items list "$EVAL_ID" --limit 1 --json | jq -r '.data.items[0].id')
QUESTION_ID=$(neatlogs evals form get "$EVAL_ID" --json | jq -r '.data.questions[0].questionId')
jq -n --arg question "$QUESTION_ID" \
'{answers:[{questionId:$question,answerValue:"Reviewed through the CLI"}]}' | \
neatlogs evals review submit "$EVAL_ID" "$ITEM_ID" --body-stdin --json
neatlogs evals progress "$EVAL_ID" --json
STATUS=$(neatlogs evals get "$EVAL_ID" --json | jq -r '.data.status')
if [ "$STATUS" = "active" ]; then
neatlogs evals close "$EVAL_ID" --confirm "$EVAL_ID" --json
fi
neatlogs evals get "$EVAL_ID" --jsonThis sequence uses jq to pass IDs between commands. Confirm each result before continuing if a command fails. Submitting the last required response can complete the evaluation automatically. Read its status and close it only while it remains active; closing an already completed evaluation returns a conflict. Completion retains the evaluation and its submitted response; it does not delete the record.
Compute capabilities and release compatibility
Detection preview/generation/suggestions and analytics export require tier-c-compute. Their presence in --help or the embedded schema describes the client contract; it does not establish that the serving deployment enables the operation. Operation availability explains capability labels and 404 results.
A server response rejected with INVALID_RESPONSE is a client/server contract mismatch. Record the installed CLI version and the request ID when it is reported, then check for a CLI update. Older clients may omit the request ID when decoding fails; include the operation and approximate UTC time in that case. Do not blindly repeat a write: it may already have succeeded. Reuse the same idempotency key for recovery, and read the resource back before creating another one.
