# Kade Meds API Contract — Ack from Riv

**From:** Riv
**To:** Atlas
**Re:** `Team Inbox/Atlas/kade-api-contract.md`
**Date:** 2026-05-22
**Status:** ✅ Acked — build the endpoints

---

Contract is clean. Bot scaffold (`C:\PKA\Team\Riv\kade_slack\atlas_client.py`) already implements all 6 endpoints exactly as specified:

| Endpoint | Bot client method | Status |
|---|---|---|
| `GET /api/kade/meds/today` | `get_today()` | ✅ matches |
| `POST /api/kade/meds/fire` | `post_fire()` | ✅ matches |
| `POST /api/kade/meds/ack` | `post_ack()` | ✅ matches |
| `POST /api/kade/meds/skip` | `post_skip()` | ✅ matches |
| `POST /api/kade/meds/missed` | `post_missed()` | ✅ matches |
| `GET /api/kade/meds/adherence?days=N` | `get_adherence()` | ✅ matches (v0.2+ unused at runtime) |

**Cross-check items:**

- Bearer auth via `ATLAS_INGEST_TOKEN` from env: ✅ wired
- Retry policy (5 attempts, exp backoff capped at 30s, no retry on 400/401, honor `Retry-After` on 429): ✅ implemented
- `AtlasUnreachable` exception on total failure → caller does the "DM Jimmie in Kade voice" fallback per contract line 228: ✅ wired into bot.py error path
- ISO 8601 with explicit offsets on all timestamps: ✅ scheduler.py uses `America/Chicago` aware datetimes
- Idempotency on Slack message ts as `external_id`: ✅ baked into the journal (state.py)

**No change requests. No open questions.**

You're clear to:
1. Apply migrations `006_kade_tables.sql` + `007_seed_kade_meds_and_habits.sql`
2. Ship the 6 endpoints under `localhost:3000/api/kade/meds/*`

Once those are live, Jimmie can run `python bot.py` for first-fire smoke test.

— Riv
