Documentation

WhenPeak API

Performance intelligence in one API call. Stateless predictions, no setup.

Quick Start

Get your first prediction in under 2 minutes.

Request
curl -X POST https://api.whenpeak.com/api/v1/predict \
  -H "Content-Type: application/json" \
  -d '{
    "sleep_time": "23:00",
    "wake_time": "07:00",
    "sleep_quality": "good"
  }'
Response
{
  "dps": 74,
  "peak_1": { "time": "10:00" },
  "peak_2": { "time": "16:00" },
  "dip":    { "time": "14:00" },
  "chronotype": "Third Bird",
  "confidence": "low",
  "upgrade_prompt": "Connect Apple Health for 3x more accurate predictions."
}

API Reference

Base URLhttps://api.whenpeak.com

Authentication

WhenPeak has two access levels.

No key needed: public prediction: POST /api/v1/predict is open. Stateless, no account required.

API key: authenticated endpoints: All personalised endpoints (/ingest/*, /performance/*, /chronotype) require a key sent in the header:

X-WhenPeak-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxx
Get a key

Key registration is handled in the developer dashboard. Create a free account at whenpeak.com/dashboard.html. Your pk_live key is issued once on signup (Free tier: 1,000 calls/month).

Your api_key is shown once. We store only a SHA-256 hash. It cannot be recovered later. Save it now. If you lose it, create another account from the dashboard.

Optional fields: omit, don't null

Send optional request fields by omitting them entirely. Do not send them as null. A standard client that serializes unset fields as null will receive a 422 error that looks like a missing required field. Omit the key instead.

Good:   {"sleep_time":"23:00","wake_time":"07:00","sleep_quality":"good"}
Avoid:  {"sleep_time":"23:00","wake_time":"07:00","sleep_quality":"good",
         "exercise_yesterday":null,"waso_minutes":null}
Verify a key
curl https://api.whenpeak.com/api/v1/auth/verify \
  -H "X-WhenPeak-API-Key: pk_live_..."

Returns {"valid": true, "user_id": "..."}. Costs 1 call against your quota.

Multi-user / B2B

If you're building an app where multiple end-users each need their own performance profile, you don't need a separate API key per user. Your developer key authenticates the account; you route each call to the right end-user with a second header.

Step 1: provision your end-users once:

curl -X POST https://api.whenpeak.com/api/v1/users \
  -H "X-WhenPeak-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"label": "alice@yourapp.com"}'
{
  "user_id": "a1b2c3...",
  "label": "alice@yourapp.com",
  "created": 888">true
}

Store the user_id in your app. It's the stable identifier for that end-user's sleep and performance history.

Step 2: route every data call to the right user:

curl -X POST https://api.whenpeak.com/api/v1/ingest/sleep \
  -H "X-WhenPeak-API-Key: pk_live_..." \
  -H "X-WhenPeak-User-ID: a1b2c3..." \
  -H "Content-Type: application/json" \
  -d '{...}'

Each end-user's data is fully isolated. All calls count against your developer quota. Management endpoints (provision / list / delete users) do not count against your quota.

Rate Limits

Each key has a monthly call quota by tier. Resets on the first of each calendar month (UTC).

TierPriceCalls / month
Free$01,000
Growth$4950,000
Scale$199500,000

The public /api/v1/predict, the usage check endpoint, and all user management endpoints (POST/GET/DELETE /api/v1/users) do not count against your quota.

Check your usage (free, doesn't count)
curl https://api.whenpeak.com/api/v1/auth/usage \
  -H "X-WhenPeak-API-Key: pk_live_..."
Returns
{
  "tier": "free",
  "used": 412,
  "limit": 1000,
  "remaining": 588,
  "period": "2026-06"
}
When quota is exceeded

Authenticated endpoints return 429 Too Many Requests:

{
  "detail": "Monthly quota exceeded for the 'free' tier (1,000 calls/month).
             Upgrade at https://whenpeak.com/#pricing."
}

Upgrade on the Pricing page or email hello@whenpeak.com.

Endpoints

Auth Endpoints

POST/api/v1/auth/register

Retired / Deprecated

This endpoint is retired and now returns 410 Gone with a message pointing to the dashboard. Create a free account at whenpeak.com/dashboard.html to receive your pk_live key.

GET/api/v1/auth/verify

Confirm a key is valid. Returns the resolved user_id. Costs 1 call.

GET/api/v1/auth/usage

Current month's usage for a key. Does not count against your quota. Returns: tier, used, limit, remaining, period.

User Management Endpoints

These endpoints let you provision and manage end-users under your developer account. Require X-WhenPeak-API-Key. Do not count against your quota.

POST/api/v1/users

Provision an isolated end-user. Idempotent. Provisioning the same label twice returns the existing user (created: false) rather than erroring.

Request body
FieldTypeRequiredDescription
labelstringYesYour identifier for this user, e.g. their email
ageintNoSets HRV baseline (default: 35)

Returns: user_id, label, created (true/false).

GET/api/v1/users

List every end-user you have provisioned. Returns: count, users[] (user_id, label, created_at).

DELETE/api/v1/users/{user_id}

Permanently delete an end-user and all of their data. Scoped to your account. You can only delete users you provisioned. Use this to honour GDPR erasure requests.

Prediction Endpoints

POST/api/v1/predict

Stateless prediction from self-reported data. No authentication required.

Request body
FieldTypeRequiredDescription
sleep_timestringRequiredHH:MM previous night, e.g. "23:00"
wake_timestringRequiredHH:MM this morning, e.g. "07:00"
sleep_qualitystringRequired"good" / "fair" / "poor"
exercise_yesterdaybooleanOptional: omit if unknownWhether user exercised yesterday
exercise_timingstringOptional: omit if unknown"morning" / "afternoon" / "evening"
sleep_latency_minutesnumberOptional: omit if unknownMinutes to fall asleep
waso_minutesnumberOptional: omit if unknownMinutes awake during the night

Optional fields must be omitted entirely when not provided: do not send as null.

GET/api/v1/performance/best-window

Find the optimal time window for a specific task type.

Parameters
ParamTypeRequiredOptions
task_typestringYesanalytical / creative / learning / administrative
durationintegerNoMinutes (default: 90)
GET/api/v1/performance/now

Get the current moment's performance score.

Returns
  • current_performance
  • window_type
  • recommendation
  • peak_1
  • dip
GET/api/v1/performance/forecast

Multi-day behavioural forecast (7–30 days). Requires an API key. Projects DPS and a performance curve for each upcoming day from your day-of-week sleep pattern, with sleep debt rolled forward. Days 1–2 high confidence, 3–7 medium, beyond a week a projection of habitual behaviour.

Parameters
ParamTypeRequiredDescription
daysintegerNoForecast horizon. Range: 7–30, default: 7
Returns
{
  "summary": {
    "average_day": "Tuesday",
    "best_day": "Wednesday",
    "worst_day": "Sunday"
  },
  "forecast": [
    {
      "day": "2026-06-11",
      "dps": 74,
      "curve": [...],
      "peak_1": { "time": "10:00" },
      "peak_2": { "time": "16:00" },
      "dip": { "time": "14:00" },
      "projected_sleep_hours": 7.5,
      "confidence": "high"
    }
  ]
}
POST/api/v1/predict/week

Stateless multi-day projection from a single self-report. No auth. Repeats today's estimate forward with decaying confidence; points users to the authenticated forecast for a behavioural projection.

Parameters
ParamTypeRequiredDescription
daysintegerNoProjection horizon. Range: 7–30, default: 7
Request body

Same as POST /api/v1/predict.

Suggestion Endpoints

POST/api/v1/performance/suggest

The model run in reverse. Prediction answers "given my behaviour, what's my curve?"; suggestion answers "given a target, what behaviour gets me there?": it works backward from a future target to tonight's actions.

Requires an API key (X-WhenPeak-API-Key). Uses the caller's stored sleep history, and is multi-user aware via the X-WhenPeak-User-ID header. Counts as 1 call against your quota.

With few or no synced nights it still returns a plan, labelled with a population_baseline confidence that matures to calibrating and then personalized as sleep history accumulates.

Request body

The body has a required target plus optional aggressiveness and constraints.

target is one of three shapes, selected by kind:

KindFieldsUse
pointdate "YYYY-MM-DD", time "HH:MM"Be at peak at a moment (interview, exam, call)
windowdate "YYYY-MM-DD", start "HH:MM", end "HH:MM"Hold capacity across a window (multi-hour exam)
shiftdirection "earlier" | "later", hours (number), by_date "YYYY-MM-DD" (optional), reason "jetlag" | "habit" (optional)Move your whole circadian phase (jet lag, or waking earlier permanently)

aggressiveness (string, optional): "minimal" (default: one gentle nudge, favours keeping your current rhythm) or "full" (travellers / hard deadlines; permits larger daily shifts).

constraints (object, optional):

FieldTypeDescription
earliest_wakestring HH:MMWon't ask you to wake before this
latest_wakestring HH:MMWon't ask you to wake after this
fixed_wakestring HH:MMWake is immovable that day (e.g. a flight)
min_sleep_hoursnumberYour sleep floor; raised to max(this, the 7h health floor), never lowered
Response
FieldDescription
shapepoint / window / shift
feasibility{ achievable, runway_days, limit_reason, headline }. limit_reason is null, "runway", "constraints", "chronotype_range", or "circadian_floor"
confidence{ label, nights_of_data, consistency_sd_min, note }. label is population_baseline / calibrating / personalized
current_stateYour estimated chronotype, peak, and typical wake
plan{ summary, nights[], event_day }: the night-by-night plan and the projected event-day outcome
tonightA single gentle action for tonight
guardrailsHealth-floor and framing guarantees (never prescribes under 7h)
disclaimerPlain-language scope note
Example request
curl -X POST https://api.whenpeak.com/api/v1/performance/suggest \
  -H "X-WhenPeak-API-Key: pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"target": {"kind": "point", "date": "2026-07-16", "time": "14:00"}}'
Example response (nights array abbreviated)
{
  "shape": "point",
  "target": { "date": "2026-07-16", "window": "14:00", "centre": "14:00" },
  "feasibility": {
    "achievable": 888">true,
    "runway_days": 13,
    "limit_reason": 888">null,
    "headline": "There's a clear plan to have you sharp across 14:00. Projected capacity ~80.8/100."
  },
  "confidence": {
    "label": "personalized",
    "nights_of_data": 14,
    "consistency_sd_min": 0.0,
    "note": "Personalised to your measured circadian phase and sleep pattern."
  },
  "current_state": {
    "estimated_chronotype": "Third Bird",
    "estimated_peak": "10:41",
    "typical_wake": "07:00"
  },
  "plan": {
    "summary": "Your rhythm already lines up well with 14:00. The plan is to arrive rested: clear sleep debt over the next 13 nights and protect the night before.",
    "nights": [
      { "date": "2026-07-04", "target_wake": "07:00", "target_bed": "23:15", "target_sleep_hours": 7.75, "focus": "clear sleep debt" },
      { "date": "2026-07-16", "target_wake": "07:00", "target_bed": "23:15", "target_sleep_hours": 7.75, "focus": "protect the night before" }
    ],
    "event_day": {
      "date": "2026-07-16",
      "target_wake": "07:00",
      "projected_peak": "10:00",
      "projected_capacity_at_target": 80.8,
      "projected_window_floor": 888">null,
      "framing": "This puts your projected capacity around 80.8/100 across 14:00: odds in your favour, not a guarantee."
    }
  },
  "tonight": {
    "action": "one_gentle_nudge",
    "detail": "Aim for lights-out around 23:15 tonight to start clearing sleep debt. Just one small step."
  },
  "guardrails": {
    "health_floor_respected": 888">true,
    "min_sleep_hours_prescribed": 7.0,
    "claims_basis": "internal_dps",
    "style": "pull_not_push"
  },
  "disclaimer": "WhenPeak suggestions raise the odds your best hours land where you need them. They are not medical advice and never ask you to cut sleep."
}

Response Format

FieldTypeDescription
dpsfloat (0.0–100.0)Daily Performance Score. Always a float: type as float/double, not int
peak_1objectFirst cognitive peak: { "time": "HH:MM", "hour": int, "value": float }
peak_2objectSecond cognitive peak: same shape as peak_1
dipobjectNatural energy dip: same shape as peak_1
chronotypestring"Lark" / "Third Bird" / "Night Owl"
confidencestring"low" / "medium" / "high"
upgrade_promptstringShown when confidence is low: prompt to connect wearable data

All score values (dps, peak value, dip value) are floats. Typing them as int will cause a validation error on real data, e.g. dps: 87.8, not 87.

Integrations

ChatGPT

Available now as a GPT. Users ask in plain English; WhenPeak predicts their peak.

Try the WhenPeak GPT →

Claude (MCP)

MCP server deployed. Remote integration rolling out.

Server URLhttps://mcp.whenpeak.com/sse

Any AI Agent

OpenAPI spec available for any tool-calling LLM:

OpenAPI spechttps://api.whenpeak.com/openapi.json

Tool definitions for Claude and OpenAI function calling are available in the GitHub repo.