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"

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 @-

Push alerts from a SIEM

curl -s -X POST "$THREEAM/v1/alerts/splunk" -H "Authorization: Bearer $INGEST_TOKEN" \
  -H 'Content-Type: application/json' --data @splunk-alert.json

Every shape and source name: Alert webhooks.

Make site keys for new sites from a script

curl -s -X POST "$THREEAM/v1/keychain/site-keys" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"labels":["Frankfurt staging","Singapore DR"],"purpose":"staging","licence":true,"hours":72}' \
  | jq -r '.keys[] | "\(.label)\t\(.key)"'

Each key is shown once and works once. Deliver it to its site by your secure channel, then on the new host: ./install.sh --keychain-join $HOME_URL --keychain-site-key-file site.key.

Alert when a Keychain member stops syncing

curl -s -H "Authorization: Bearer $TOKEN" "$THREEAM/v1/keychain" \
  | jq -r '.members[] | select(.health=="silent" or .health=="gone") | "\(.label) \(.health) \(.quiet_s)s"'

Or follow the audit log for keychain.member_silent and keychain.member_gone (syslog forwards security events).

Run a Study every week

curl -s -X POST "$THREEAM/v1/study" -H "Authorization: Bearer $TOKEN" -d '{"minutes":30}'   # 409 if one is running

Find incidents with the query language

curl -sG "$THREEAM/v1/episodes" -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'query=service:core-banking result:handed_over opened<7d' --data-urlencode 'limit=50'

On this page