Paceo
FOR AI AGENTS

Workout Link Spec.

Copy the spec below and paste it into any AI agent: ChatGPT, Claude, Gemini, or your own. Describe the workout you want and the agent will produce a paceo:// link. Tap it on your iPhone and the workout opens in Paceo ready to save or schedule.

Links open the Paceo iOS app. The app is required; links won't work in a browser.
PACEO WORKOUT LINK SPEC v1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Generate deep links that open workout previews directly in the Paceo iOS app.
The user taps the link → Paceo shows the workout → they save or schedule it.

─── URL FORMAT ─────────────────────────────────────────────────────────────────

  paceo://workout?d={encoded_payload}

─── ENCODING ───────────────────────────────────────────────────────────────────

  1. Build a JSON payload (schema below)
  2. Compress with zlib (deflate, RFC 1950)
  3. Encode as URL-safe base64: replace + → -  and  / → _  and strip trailing =

  Python:
    import json, zlib, base64
    def make_link(name: str, plan: dict) -> str:
        payload = json.dumps({"v": 1, "n": name, "p": plan}, separators=(",", ":"))
        compressed = zlib.compress(payload.encode())
        b64 = base64.b64encode(compressed).decode().replace("+","-").replace("/","_").rstrip("=")
        return f"paceo://workout?d={b64}"

  Node.js (requires pako: npm install pako):
    import pako from "pako"
    function makeLink(name, plan) {
      const bytes = pako.deflate(JSON.stringify({ v: 1, n: name, p: plan }))
      let bin = ""; bytes.forEach(b => bin += String.fromCharCode(b))
      return "paceo://workout?d=" + btoa(bin).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")
    }

─── PAYLOAD SCHEMA ─────────────────────────────────────────────────────────────

  {
    "v": 1,         // version, always 1
    "n": string,    // workout name (required, non-empty)
    "p": WorkoutPlan
  }

─── WorkoutPlan ────────────────────────────────────────────────────────────────

  Three types: "goal", "pacer", "custom"

  TYPE 1: goal-based workout
  { "type": "goal",
    "workout": {
      "activity":          ActivityType,
      "location":          LocationType,
      "swimmingLocation"?: SwimmingLocationType,   // only when activity is swimming
      "goal":              WorkoutGoal
    }
  }

  TYPE 2: pacer (finish a distance in a target time)
  { "type": "pacer",
    "workout": {
      "activity":  ActivityType,
      "location":  LocationType,
      "distance":  { "value": number, "unit": DistanceUnit },
      "time":      { "value": number, "unit": TimeUnit }
    }
  }

  TYPE 3: custom interval workout
  { "type": "custom",
    "workout": {
      "activity":     ActivityType,
      "location":     LocationType,
      "displayName"?: string,
      "warmup"?:      WorkoutStep,
      "blocks":       IntervalBlock[],
      "cooldown"?:    WorkoutStep
    }
  }

─── WorkoutGoal ────────────────────────────────────────────────────────────────

  { "type": "open" }
  { "type": "time",     "value": number, "unit": TimeUnit }
  { "type": "distance", "value": number, "unit": DistanceUnit }
  { "type": "energy",   "value": number, "unit": EnergyUnit }

─── WorkoutStep ────────────────────────────────────────────────────────────────

  { "goal": WorkoutGoal, "alert"?: WorkoutAlert }

─── WorkoutAlert (optional coaching zone) ──────────────────────────────────────

  { "type":   "heartRate" | "pace" | "speed" | "power" | "cadence",
    "target": { "min": number, "max": number, "unit": string },
    "metric": "current" | "average" | "maximum" }

  Common units by alert type:
    heartRate → "bpm"
    pace      → "min/km"  or  "min/mi"
    speed     → "km/h"    or  "mph"
    power     → "watts"
    cadence   → "rpm"  (cycling)  or  "spm"  (running)

─── IntervalBlock / IntervalStep ───────────────────────────────────────────────

  IntervalBlock : { "steps": IntervalStep[], "iterations": number }
  IntervalStep  : { "purpose": "work" | "recovery", "step": WorkoutStep }

─── ENUMS ──────────────────────────────────────────────────────────────────────

  ActivityType:
    running, cycling, swimming, walking,
    golf, tennis, basketball, soccer, hockey, baseball, softball, volleyball,
    badminton, tableTennis, squash, racquetball, lacrosse, cricket, rugby,
    football, americanFootball, australianFootball,
    gymnastics, boxing, kickboxing, martialArts,
    pilates, yoga, dance, danceInspiredTraining, barre,
    coreTraining, flexibility, strengthTraining, traditionalStrengthTraining,
    functionalStrengthTraining, crossTraining, mixedCardio, handCycling,
    cardioDance, socialDance, cardioCooldown, cooldown, other

  LocationType         : "indoor" | "outdoor" | "unknown"
  SwimmingLocationType : "pool" | "openWater" | "unknown"
  TimeUnit             : "seconds" | "minutes" | "hours"
  DistanceUnit         : "meters" | "kilometers" | "yards" | "miles" | "feet"
  EnergyUnit           : "calories" | "kilocalories" | "joules" | "kilojoules"

─── EXAMPLES ───────────────────────────────────────────────────────────────────

  # 10 km outdoor run with distance goal
  make_link("10K Run", {
    "type": "goal",
    "workout": {
      "activity": "running",
      "location": "outdoor",
      "goal": { "type": "distance", "value": 10, "unit": "kilometers" }
    }
  })

  # 5 × 4 min hard / 2 min easy: VO2max running intervals
  make_link("VO2max Intervals", {
    "type": "custom",
    "workout": {
      "activity": "running",
      "location": "outdoor",
      "warmup":   { "goal": { "type": "time", "value": 10, "unit": "minutes" } },
      "blocks": [{
        "iterations": 5,
        "steps": [
          { "purpose": "work",
            "step": {
              "goal":  { "type": "time", "value": 4, "unit": "minutes" },
              "alert": { "type": "heartRate",
                         "target": { "min": 155, "max": 175, "unit": "bpm" },
                         "metric": "current" }
            }
          },
          { "purpose": "recovery",
            "step": { "goal": { "type": "time", "value": 2, "unit": "minutes" } }
          }
        ]
      }],
      "cooldown": { "goal": { "type": "time", "value": 5, "unit": "minutes" } }
    }
  })

  # 30-min steady cycling pacer at 200W
  make_link("Tempo Ride", {
    "type": "pacer",
    "workout": {
      "activity":  "cycling",
      "location":  "indoor",
      "distance":  { "value": 15, "unit": "kilometers" },
      "time":      { "value": 30, "unit": "minutes" }
    }
  })

  # 1500m pool swim with open goal
  make_link("1500m Swim", {
    "type": "goal",
    "workout": {
      "activity":          "swimming",
      "location":          "indoor",
      "swimmingLocation":  "pool",
      "goal": { "type": "distance", "value": 1500, "unit": "meters" }
    }
  })
1
Copy the spec
Hit copy spec above and paste it into any AI chat or system prompt.
2
Describe your workout
Ask the agent to generate a link: “5×1km intervals at threshold pace with a 10 min warmup”.
3
Tap the link
Open the paceo:// link on your iPhone. Review the workout and save or schedule it.