Set these once at the top of your shell session:
export BASE=https://client-api.stg.future-demand.com/api/v3
export FD_USER=you@partner.com
export FD_PASS='...'

Auth — sign in and stash tokens

RESP=$(curl -s -X POST "$BASE/auth" \
  -H "Content-Type: application/json" \
  -d "{\"username\":\"$FD_USER\",\"password\":\"$FD_PASS\"}")

export FD_ACCESS_TOKEN=$(echo "$RESP"  | jq -r .AccessToken)
export FD_ID_TOKEN=$(echo "$RESP"      | jq -r .IdToken)
export FD_REFRESH_TOKEN=$(echo "$RESP" | jq -r .RefreshToken)

# Extract the first partner-id from cognito:groups
export FD_PARTNER_ID=$(echo "$FD_ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null \
  | jq -r '."cognito:groups"[]' | grep -E '^(partner-id-|fd[0-9]+)' | head -1)

echo "Signed in as $FD_USER, partner $FD_PARTNER_ID"
A reusable header alias:
alias fd='curl -s -H "Authorization: Bearer $FD_ACCESS_TOKEN" -H "X-Preferred-Partner-Id: $FD_PARTNER_ID"'

Refresh

curl -s -X POST "$BASE/auth/refresh_token" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$FD_REFRESH_TOKEN\"}" \
  | jq

Partner / profile

fd "$BASE/partners/"   | jq
fd "$BASE/profile/"    | jq
fd "$BASE/features/"   | jq
fd "$BASE/permissions" | jq

Events — list and detail

Demand prediction uses lookout_prediction_benchmark / lookout_prediction with a required type=capacity|ticket_count.
fd "$BASE/events/?page=1&limit=5&descending=false" | jq

EID=$(fd "$BASE/events/?limit=1" | jq -r '.items[0].id')
echo "Picked $EID"

fd "$BASE/events/$EID"                                              | jq
fd "$BASE/events/$EID/lookout_prediction_benchmark?type=capacity"  | jq
fd "$BASE/events/$EID/lookout_prediction?type=capacity"            | jq
fd "$BASE/events/$EID/tickets"                                     | jq
fd "$BASE/events/$EID/campaigns?limit=10"                          | jq

# Optional / diagnostic — valid APIs, but not part of the core Lookout or
# Wave reference flows:
fd "$BASE/events/$EID/statistics" | jq
fd "$BASE/events/$EID/clusters"   | jq

Cluster recommendations

Goal values are enum-style: UTILIZATION, ROAS, VISIBILITY, LINK_CLICKS.
fd "$BASE/events/$EID/suggested_tcs?budget=5000&goal=UTILIZATION"           | jq
fd "$BASE/events/$EID/campaigns_expected_value?budget=5000&goal=UTILIZATION" | jq
fd "$BASE/events/$EID/budget_min_max?runtime_in_days=14"                    | jq
fd "$BASE/events/$EID/default_campaigns_parameters"                         | jq
fd "$BASE/setup_processes/call_to_action/?goal=UTILIZATION&has_pixel=true&lead_form_id=" | jq

Wave — campaigns lifecycle

# Read existing draft (404 = no draft)
fd "$BASE/setup_processes/$EID" | jq

# Create draft (illustrative — see the campaigns-lifecycle guide for the
# full payload structure; creatives/targeting/integration_details are arrays)
fd -X POST "$BASE/setup_processes/$EID" \
  -H "Content-Type: application/json" \
  -d '{
    "goal":         "UTILIZATION",
    "total_budget": 5000,
    "start_date":   "2026-05-25",
    "end_date":     "2026-06-12",
    "tc_run_id":    42,
    "creatives":            [],
    "targeting":            [],
    "integration_details":  []
  }' | jq

# Publish
fd -X POST "$BASE/setup_processes/$EID/boost" | jq

# Edit one published cluster
fd -X POST "$BASE/campaigns_setup" \
  -H "Content-Type: application/json" \
  -d "{
    \"eid\":       \"$EID\",
    \"tc\":        \"family_outings_munich\",
    \"tc_run_id\":  42,
    \"audience_id\":\"aud_777\",
    \"creatives\":  [],
    \"targeting\":  []
  }" | jq

# Cancel draft
fd -X DELETE "$BASE/setup_processes/$EID" | jq

Attribution

The reference webapp reads campaign results from the Facebook integration endpoint, not the generic /campaign_results/. Partner scope flows through the X-Preferred-Partner-Id header.
fd "$BASE/integrations/facebook/events/$EID/campaign_results" | jq
fd "$BASE/integrations/facebook/events/$EID/fb_insights"      | jq
fd "$BASE/events/$EID/attribution_model"                      | jq

Messages / Notifications

These are two different surfaces: /messages/* is the per-partner messages API; /notifications/* is the per-user notification feed (the one the production webapp actually routes to). They are not a single inbox. Note totalunread returns { "total_unread": N }.
fd "$BASE/messages/partner/?limit=10&page=1"   | jq
fd "$BASE/messages/totalunread"                | jq   # → { "total_unread": N }
fd "$BASE/notifications/?limit=10"             | jq
fd "$BASE/notifications/stats"                 | jq

Sales — manual ingest

fd -X POST "$BASE/ingest/transaction_summary" \
  -H "Content-Type: application/json" \
  -d "{
    \"eid\":  \"$EID\",
    \"from\": \"2026-06-01\",
    \"to\":   \"2026-06-12\",
    \"tickets\": [
      { \"type\": \"standard\", \"sold\": 412, \"revenue\": 18540 }
    ]
  }" | jq

File upload — XLSX ingest

curl -s -X POST "$BASE/ingest/transaction_summary/from_excel?eid=$EID" \
  -H "Authorization: Bearer $FD_ACCESS_TOKEN" \
  -H "X-Preferred-Partner-Id: $FD_PARTNER_ID" \
  -F "file=@./sales.xlsx" \
  | jq

Media upload (note: no /v3 prefix)

POST /media is a valid endpoint, but campaign creatives in the reference webapp are uploaded via storage multipart (/storage/...), not /media. Use this for ad-hoc uploads; see File uploads for the creative path.
# /media is on the API host but WITHOUT /v3 (path becomes /api/media)
MEDIA_BASE="${BASE%/v3}"

curl -s -X POST "$MEDIA_BASE/media" \
  -H "Authorization: Bearer $FD_ACCESS_TOKEN" \
  -H "X-Preferred-Partner-Id: $FD_PARTNER_ID" \
  -F "file=@./creative.jpg" \
  | jq

Health

curl -s "$BASE/status/" | jq      # no auth