Recipes

Copy-paste automation with the HTTP API, from CI pipelines to your SIEM.

All recipes assume:

export THREEAM=https://3am.bank.internal      # your console address
export TOKEN=…                                # the admin token (keep it in your CI's secret store)

Pause 3AM during a deployment

Halt before the change, resume after. A halt refuses every action, including fixes that were already approved but not yet run. Diagnosis keeps going.

.gitlab-ci.yml
deploy:
  script:
    - 'curl -sf -X POST "$THREEAM/v1/halt" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"
         -d "{\"by\": \"ci:$CI_PROJECT_PATH\", \"reason\": \"deploying $CI_COMMIT_SHORT_SHA\"}"'
    - ./deploy.sh
  after_script:
    - 'curl -sf -X POST "$THREEAM/v1/resume" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"
         -d "{\"by\": \"ci:$CI_PROJECT_PATH\"}"'

after_script runs even when the deploy fails, so 3AM never stays halted by accident. The halt shows in the console and the audit log with your reason.

Record changes made outside 3AM

Post deploys and manual fixes to the audit log, so incidents show them in their timeline. This uses the ingest token, not the admin token.

curl -sf -X POST "$THREEAM/v1/events" -H "Authorization: Bearer $INGEST_TOKEN" -H 'Content-Type: application/json' \
  -d "{\"type\": \"human.deploy\", \"actor\": \"ci:payments\", \"reason\": \"deployed payments $SHA\",
       \"data\": {\"service\": \"payments\", \"version\": \"$SHA\"}}"

Export the audit log to your SIEM

Ask for everything after the last event you shipped (after_seq), oldest first, 2000 at a time. Run it on a schedule:

export-ledger.sh
#!/usr/bin/env bash
set -euo pipefail
last=$(cat .last_seq 2>/dev/null || echo 0)
while :; do
  page=$(curl -sf -H "Authorization: Bearer $TOKEN" "$THREEAM/v1/ledger?after_seq=$last&order=asc&limit=2000")
  [ "$(jq '.events | length' <<<"$page")" -gt 0 ] || break
  jq -c '.events[]' <<<"$page" | curl -sf -X POST "$SPLUNK_HEC/services/collector/raw?sourcetype=3am:ledger" \
    -H "Authorization: Splunk $HEC_TOKEN" --data-binary @-
  last=$(jq '.events[-1].seq' <<<"$page"); echo "$last" > .last_seq
  [ "$(jq .more <<<"$page")" = true ] || break
done

Before handing an export to an auditor, verify the chain on the source: curl -s -H "Authorization: Bearer $TOKEN" $THREEAM/v1/ledger/verify.

Alert before the licence expires

/v1/status needs no token. With the blackbox exporter's JSON prober, or a small script:

expires=$(curl -sf "$THREEAM/v1/status" | jq -r .licence.expires_at)
days=$(( ( $(date -d "$expires" +%s) - $(date +%s) ) / 86400 ))
[ "$days" -gt 30 ] || echo "3AM licence expires in $days days"

On macOS use date -j -f "%Y-%m-%dT%H:%M:%S" "${expires%%+*}" +%s instead of date -d.

List today's incidents and outcomes

curl -sf -H "Authorization: Bearer $TOKEN" "$THREEAM/v1/episodes?limit=200" \
  | jq -r '.episodes[] | select(.t_open > (now - 86400 | todate)) | [.t_open, .service, (.alerts|join(",")), .decision, .outcome] | @tsv'
Output
2026-10-04T17:54:12Z	platform	DependencyPortUnreachable	abstain	escalated
2026-10-04T16:21:03Z	core-banking-db	MySQLReadOnly	act	mitigated

Post the daily digest to a channel

text=$(curl -sf -H "Authorization: Bearer $TOKEN" "$THREEAM/v1/digest?days=1" | jq -r .markdown)
jq -n --arg text "$text" '{text: $text}' | curl -sf -X POST "$SLACK_WEBHOOK" -H 'Content-Type: application/json' -d @-

On this page