Template gallery / progress-report

Client progress report template

Client progress report written like a document a coach signed rather than a list a machine produced: the verdict as a lead paragraph beside the block's numbers as a ruled key, a start / now / change table grouped by measurement type whose change column carries the word better or worse beside its sign, the goals set last time and what happened to each, the coach's notes as real prose at a readable measure, what went well beside what to watch, the next block as a tick list, and an optional sign-off that never orphans.

chromium default PDF 20 top-level fields Billed per page

Rendered preview of the progress-report template
Rendered by Galley from the example payload below, at 816px wide , scale 2.

Fields

The full JSON Schema is below. Required fields are marked; anything missing comes back as a 422 naming the path, the expected type and a value that would be accepted.

Field Type Required Description
accent string optional The one hot accent: a worse change, the "what to watch" rule and the rule over the client quote. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.
title string optional
client object { name, programme } required
coach object { name, business, email, logo_url } optional
period object { label, from, to } required
headline string optional The one paragraph they will read if they read nothing else. Say the honest version.
summary_cards array of object { label, value, note } optional The four or five numbers that summarise the block, printed as a ruled key beside the lead paragraph rather than as a row of boxes.
metrics_heading string optional
before_label string optional
after_label string optional
groups array of object { title, metrics } optional Measurements grouped into sections. Values are strings so you can carry units, times and reps in the same table without the template pretending to do maths on them.
goals array of object { goal, status, direction, progress, … } optional What was agreed last time, and what happened.
notes_heading string optional
coach_notes array of string optional
quote object { text, who } optional Something the client said, in their own words.
wins array of string optional
watch array of string optional
next_block object { name, dates, summary, targets } optional
sign_off boolean optional Adds two signature lines, for programmes where the client signs off on the review.
footer string optional Replaces the default measurement-conditions line.

Default render options

Applied unless you override them. Options are part of the cache key.

page_size "Letter"
margin "0.6in"
print_background true

Example payload

This exact object renders the preview above. It is also what the API echoes back in a validation error, so an agent can repair a payload without a round trip to the docs.

example.jsonjson
{
  "title": "Twelve-week progress report",
  "client": {
    "name": "Marcus Oyelaran",
    "programme": "Strength and conditioning, two sessions a week"
  },
  "coach": {
    "name": "Dana Whitfield-Orr",
    "business": "Northbank Strength",
    "email": "dana@northbankstrength.test"
  },
  "period": {
    "label": "Block 2 · weeks 13–24",
    "from": "2026-06-22",
    "to": "2026-09-13"
  },
  "headline": "Twenty-one of twenty-four sessions attended, up from seventeen last block, and every main lift is up. The squat is the s… [343 characters, shortened for display]",
  "summary_cards": [
    {
      "label": "Sessions",
      "value": "21 / 24",
      "note": "88% attendance, up from 71%"
    },
    {
      "label": "Total load lifted",
      "value": "412,000 lb",
      "note": "Up 31% on block 1"
    },
    {
      "label": "Bodyweight",
      "value": "196 lb",
      "note": "Down 4 lb from 200"
    },
    {
      "label": "Resting HR",
      "value": "58 bpm",
      "note": "Down 7 from 65"
    }
  ],
  "before_label": "Jun 22",
  "after_label": "Sep 13",
  "groups": [
    {
      "title": "Strength",
      "metrics": [
        {
          "name": "Back squat, 5RM",
          "unit": "lb",
          "before": "185",
          "after": "235",
          "change": "+50",
          "direction": "better",
          "note": "Depth improved at the same time, which makes the jump bigger than it looks"
        },
        {
          "name": "Deadlift, 3RM",
          "unit": "lb",
          "before": "275",
          "after": "315",
          "change": "+40",
          "direction": "better",
          "note": "Bar path is straighter; you stopped rounding at the knee"
        },
        {
          "name": "Bench press, 5RM",
          "unit": "lb",
          "before": "155",
          "after": "175",
          "change": "+20",
          "direction": "better",
          "note": "Slowest of the three, and expected given the shoulder"
        },
        {
          "name": "Overhead press, 5RM",
          "unit": "lb",
          "before": "95",
          "after": "105",
          "change": "+10",
          "direction": "better",
          "note": "Held back deliberately while the left shoulder settled"
        },
        {
          "name": "Chin-ups, best set",
          "unit": "reps",
          "before": "4",
          "after": "9",
          "change": "+5",
          "direction": "better",
          "note": "Full range, no kipping"
        }
      ]
    },
    {
      "title": "Body composition",
      "metrics": [
        {
          "name": "Bodyweight",
          "unit": "lb",
          "before": "200",
          "after": "196",
          "change": "−4",
          "direction": "better",
          "note": "Same scale, Tuesday mornings, before breakfast"
        },
        {
          "name": "Waist",
          "unit": "in",
          "before": "36.5",
          "after": "35.0",
          "change": "−1.5",
          "direction": "better",
          "note": "Measured at the navel, standing relaxed"
        },
        {
          "name": "Chest",
          "unit": "in",
          "before": "41.0",
          "after": "42.0",
          "change": "+1.0",
          "direction": "better"
        },
        {
          "name": "Upper arm, right",
          "unit": "in",
          "before": "13.5",
          "after": "14.25",
          "change": "+0.75",
          "direction": "better"
        }
      ]
    },
    {
      "title": "Movement and conditioning",
      "metrics": [
        {
          "name": "Overhead squat screen",
          "unit": "0–3",
          "before": "1",
          "after": "2",
          "change": "+1",
          "direction": "better",
          "note": "Heels stay down now; arms still drift forward at the bottom"
        },
        {
          "name": "Left shoulder flexion",
          "unit": "deg",
          "before": "155",
          "after": "168",
          "change": "+13",
          "direction": "better",
          "note": "Right side is 178, so there is still a gap"
        },
        {
          "name": "500 m row",
          "unit": "time",
          "before": "1:52",
          "after": "1:44",
          "change": "−8 s",
          "direction": "better"
        },
        {
          "name": "Sleep, self-reported",
          "unit": "h/night",
          "before": "6.2",
          "after": "6.4",
          "change": "+0.2",
          "note": "Barely moved. This is the one we did not fix"
        }
      ]
    }
  ],
  "goals": [
    {
      "goal": "Squat 225 lb for five",
      "status": "Met",
      "direction": "better",
      "progress": 100,
      "note": "Hit 235 in week 21, two weeks ahead of schedule"
    },
    {
      "goal": "Ten unbroken chin-ups",
      "status": "Nearly",
      "progress": 90,
      "note": "Nine in week 23. Tenth is a formality in block 3"
    },
    {
      "goal": "Attend 22 of 24 sessions",
      "status": "Missed by one",
      "progress": 88,
      "note": "Three missed: two work trips and one you told me about in advance, which counts for something"
    },
    {
      "goal": "Seven hours of sleep a night",
      "status": "Not met",
      "direction": "worse",
      "progress": 30,
      "note": "6.4 on average. We have talked about this more than any lift and it is still the biggest lever you are not pulling"
    }
  ],
  "coach_notes": [
    "The attendance is the real story of this block. Seventeen sessions became twenty-one, and everything in the table above … [244 characters, shortened for display]",
    "The squat progression worked because we stopped chasing the number in week 14 and spent three weeks at 185 fixing the depth. That felt like standing still at the time. It was not: the 235 in week 21 was a better rep than the 185 in week 13.",
    "The shoulder is the thing I want to keep an eye on. It is better — thirteen degrees of flexion back and no pain reported… [401 characters, shortened for display]",
    "Sleep is the one thing that did not move. Six point four hours will cap what block 3 can do, and it is cheaper than anyt… [261 characters, shortened for display]"
  ],
  "quote": {
    "text": "I carried both kids up the stairs at once last week and did not think about it until afterwards. That has not been true for about three years.",
    "who": "Marcus, week 19"
  },
  "wins": [
    "Every main lift up, and none of them bought with worse technique",
    "Attendance up seventeen points, which is the reason for everything else",
    "Four pounds down and an inch and a half off the waist while the lifts went up",
    "No missed sessions at all in weeks 17 to 24",
    "Shoulder pain gone since week 18"
  ],
  "watch": [
    "Left shoulder is still thirteen degrees short of the right overhead",
    "Sleep has not moved and is now the limiting factor",
    "Bench is the slowest lift and will stay that way until the shoulder closes the gap",
    "Two of the three missed sessions were the same Tuesday work slot; consider moving it",
    "Arms still drift forward at the bottom of the overhead squat"
  ],
  "next_block": {
    "name": "Block 3 — strength with a hypertrophy bias",
    "dates": "Sep 21 – Dec 13, 2026",
    "summary": "The same two committed sessions a week, with a third optional day that is yours to take or leave. More volume per session, overhead work added back gradually, and the first deload in week 30 rather than week 34.",
    "targets": [
      "Back squat 255 lb for five by week 36",
      "Ten unbroken chin-ups, which is one rep away",
      "Overhead press 120 lb for five, only if the shoulder stays quiet",
      "Left shoulder flexion within five degrees of the right",
      "Six and a half hours of sleep on at least five nights a week, tracked honestly",
      "Attendance of 22 of 24 again, with the Tuesday slot moved to Wednesday"
    ]
  },
  "sign_off": true,
  "footer": "Measurements were taken under the same conditions each time: same scale on Tuesday mornings before breakfast, same tape … [305 characters, shortened for display]"
}

Values longer than 240 characters — data URIs, inline SVG — are shortened here so the shape of the payload stays readable, and the replacement says how much was left out. get_template and GET /v1/templates/:ref return them in full.

Render it

With an API keyshell
curl -sS https://api.galleyrender.com/v1/render \
  -H "Authorization: Bearer $GALLEY_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
  "template": "progress-report@1",
  "format": "pdf",
  "data": "… the example payload above …"
}'
With no API key, over MCPshell
# No API key. The first call mints a trial of 10 PDF pages or 10 images.
curl -sS https://mcp.galleyrender.com/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "render",
      "arguments": {
        "template": "progress-report",
        "format": "pdf",
        "data": { … see the example payload above … }
      }
    }
  }'

Pin the version — progress-report@1 — in anything you ship. Publishing a new version never changes an old one. Render endpoint reference →

Try a free tool

Renders the example payload on the production service. No account, no key.

JSON Schema

schema.json (20 top-level properties)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Client progress report",
  "type": "object",
  "required": [
    "client",
    "period"
  ],
  "additionalProperties": true,
  "properties": {
    "accent": {
      "type": "string",
      "description": "The one hot accent: a worse change, the \"what to watch\" rule and the rule over the client quote. A hex colour or a CSS colour keyword. It is interpolated into the stylesheet, so nothing else is accepted.",
      "examples": [
        "#d62828"
      ],
      "default": "#d62828",
      "pattern": "^(#[0-9a-fA-F]{3,8}|[a-zA-Z]{3,20})$"
    },
    "title": {
      "type": "string",
      "default": "Progress report",
      "examples": [
        "Twelve-week progress report"
      ]
    },
    "client": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "examples": [
            "Marcus Oyelaran"
          ]
        },
        "programme": {
          "type": "string",
          "examples": [
            "Strength and conditioning, two sessions a week"
          ]
        }
      }
    },
    "coach": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "examples": [
            "Dana Whitfield-Orr"
          ]
        },
        "business": {
          "type": "string",
          "examples": [
            "Northbank Strength"
          ]
        },
        "email": {
          "type": "string",
          "format": "email",
          "examples": [
            "dana@northbankstrength.test"
          ]
        },
        "logo_url": {
          "type": "string",
          "format": "uri",
          "examples": [
            "https://example.com/northbank.png"
          ]
        }
      }
    },
    "period": {
      "type": "object",
      "required": [
        "label"
      ],
      "properties": {
        "label": {
          "type": "string",
          "examples": [
            "Block 2 · weeks 13–24"
          ]
        },
        "from": {
          "type": "string",
          "format": "date",
          "examples": [
            "2026-06-22"
          ]
        },
        "to": {
          "type": "string",
          "format": "date",
          "examples": [
            "2026-09-13"
          ]
        }
      }
    },
    "headline": {
      "type": "string",
      "description": "The one paragraph they will read if they read nothing else. Say the honest version.",
      "examples": [
        "Twelve weeks, twenty-one of twenty-four sessions attended, and every main lift up."
      ]
    },
    "summary_cards": {
      "type": "array",
      "description": "The four or five numbers that summarise the block, printed as a ruled key beside the lead paragraph rather than as a row of boxes.",
      "items": {
        "type": "object",
        "required": [
          "label",
          "value"
        ],
        "properties": {
          "label": {
            "type": "string",
            "examples": [
              "Sessions"
            ]
          },
          "value": {
            "type": "string",
            "examples": [
              "21 / 24"
            ]
          },
          "note": {
            "type": "string",
            "examples": [
              "88% attendance, up from 71%"
            ]
          }
        }
      }
    },
    "metrics_heading": {
      "type": "string",
      "default": "Where you started, where you are",
      "examples": [
        "Where you started, where you are"
      ]
    },
    "before_label": {
      "type": "string",
      "default": "Start",
      "examples": [
        "Jun 22"
      ]
    },
    "after_label": {
      "type": "string",
      "default": "Now",
      "examples": [
        "Sep 13"
      ]
    },
    "groups": {
      "type": "array",
      "description": "Measurements grouped into sections. Values are strings so you can carry units, times and reps in the same table without the template pretending to do maths on them.",
      "items": {
        "type": "object",
        "required": [
          "title",
          "metrics"
        ],
        "properties": {
          "title": {
            "type": "string",
            "examples": [
              "Strength"
            ]
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "before",
                "after"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "examples": [
                    "Back squat, 5RM"
                  ]
                },
                "unit": {
                  "type": "string",
                  "examples": [
                    "lb"
                  ]
                },
                "before": {
                  "type": "string",
                  "examples": [
                    "185"
                  ]
                },
                "after": {
                  "type": "string",
                  "examples": [
                    "235"
                  ]
                },
                "change": {
                  "type": "string",
                  "description": "Write the change yourself, with its sign. A drop in resting heart rate is an improvement and a drop in squat is not; only you know which.",
                  "examples": [
                    "+50"
                  ]
                },
                "direction": {
                  "type": "string",
                  "description": "Marks the change better or worse. The word is printed under the figure as well as colouring it, so the distinction survives a black-and-white printer. Leave it off for neutral.",
                  "enum": [
                    "",
                    "better",
                    "worse"
                  ],
                  "examples": [
                    "better"
                  ]
                },
                "note": {
                  "type": "string",
                  "examples": [
                    "Depth improved at the same time, which makes the jump bigger than it looks"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "goals": {
      "type": "array",
      "description": "What was agreed last time, and what happened.",
      "items": {
        "type": "object",
        "required": [
          "goal",
          "status"
        ],
        "properties": {
          "goal": {
            "type": "string",
            "examples": [
              "Squat 225 lb for five"
            ]
          },
          "status": {
            "type": "string",
            "examples": [
              "Met"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "",
              "better",
              "worse"
            ],
            "examples": [
              "better"
            ]
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Draws a bar under the status.",
            "examples": [
              100
            ]
          },
          "note": {
            "type": "string",
            "examples": [
              "Hit 235 in week 21, two weeks ahead"
            ]
          }
        }
      }
    },
    "notes_heading": {
      "type": "string",
      "default": "Coach's notes",
      "examples": [
        "Coach's notes"
      ]
    },
    "coach_notes": {
      "type": "array",
      "items": {
        "type": "string",
        "examples": [
          "The attendance is the story of this block."
        ]
      },
      "examples": [
        [
          "The attendance is the story of this block."
        ]
      ]
    },
    "quote": {
      "type": "object",
      "description": "Something the client said, in their own words.",
      "properties": {
        "text": {
          "type": "string",
          "examples": [
            "I carried both kids up the stairs at once last week and did not think about it until afterwards."
          ]
        },
        "who": {
          "type": "string",
          "examples": [
            "Marcus, week 19"
          ]
        }
      }
    },
    "wins": {
      "type": "array",
      "items": {
        "type": "string",
        "examples": [
          "Every main lift up, none of them at the cost of technique"
        ]
      },
      "examples": [
        [
          "Every main lift up"
        ]
      ]
    },
    "watch": {
      "type": "array",
      "items": {
        "type": "string",
        "examples": [
          "Left shoulder still clicks overhead; it has not got worse but it has not gone"
        ]
      },
      "examples": [
        [
          "Left shoulder still clicks overhead"
        ]
      ]
    },
    "next_block": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "examples": [
            "Block 3 — strength with a hypertrophy bias"
          ]
        },
        "dates": {
          "type": "string",
          "examples": [
            "Sep 21 – Dec 13, 2026"
          ]
        },
        "summary": {
          "type": "string",
          "examples": [
            "Same two sessions a week, more volume per session and a third optional day."
          ]
        },
        "targets": {
          "type": "array",
          "items": {
            "type": "string",
            "examples": [
              "Back squat 255 for five by week 36"
            ]
          },
          "examples": [
            [
              "Back squat 255 for five"
            ]
          ]
        }
      }
    },
    "sign_off": {
      "type": "boolean",
      "default": false,
      "description": "Adds two signature lines, for programmes where the client signs off on the review.",
      "examples": [
        true
      ]
    },
    "footer": {
      "type": "string",
      "description": "Replaces the default measurement-conditions line.",
      "examples": [
        "Measurements were taken under the same conditions each time."
      ]
    }
  }
}