Skip to content

Developers

Your plan is yours to build on: an API with tokens you make yourself, webhooks that say when anything changes, and a connector that puts Madison inside Claude and ChatGPT.

  1. Start with a token

    In the planner, open Settings and find Developers. Name a token for what will use it, choose whether it may make changes, and copy it: Madison keeps only a fingerprint, so it is shown once. A token belongs to one planner (Students or Professional), acts as you, and stops working the moment you revoke it. Each one may make 1,200 requests an hour.

    curl https://www.usemadi.com/api/v1/today \
      -H "Authorization: Bearer mad_pat_…" \
      -H "X-Timezone: America/New_York"

    Dates are YYYY-MM-DD and times HH:MM, in your own time zone, exactly as the planner keeps them. Send X-Timezone (or ?tz=) so “today” is your today; otherwise the zone your reminders use is assumed.

  2. The REST API

    Everything lives under https://www.usemadi.com/api/v1. Answers are { data }; a problem is { error, message } with a status that means it (400, 401, 402 when a trial has ended, 403 for a read-only token, 404, 409, 429 with Retry-After). Changes are saved straight to your account, merge with edits on your other devices, and reach your webhooks. The whole description, for code generators and Zapier, is at /api/v1/openapi.json (OpenAPI 3.1).

    MethodPathWhat it does
    get/meWho the token belongs to
    get/termsEvery term
    get/coursesCourses (or projects) in a term
    get/courses/{id}One course: due in the next three weeks, this week's planned study, time studied
    get/itemsItems, filtered
    post/itemsAdd an item
    get/items/{id}One item
    patch/items/{id}Change an item
    delete/items/{id}Delete an item (to the trash, for 30 days)
    post/items/{id}/completeTick an item off (or back on)
    get/commitmentsFixed commitments in a term
    post/commitmentsAdd a fixed commitment
    delete/commitments/{id}Remove a fixed commitment
    get/sessionsThe study log: time actually spent
    post/sessionsLog a study session
    get/gradesGrades so far, per course
    get/todayToday: due, overdue, next, sessions, fixed hours
    get/upcomingOpen items due soon
    get/snapshotEverything a widget shows, in one answer
    get/forecastThe time cushion: hours needed against hours free, week by week
    get/timerThe focus timer, if one is running or paused
    post/timerStart, pause, resume or stop the focus timer
    post/captureAdd to Madison from a page or a selection
    get/webhooksThis planner's webhooks
    post/webhooksSubscribe an https address
    delete/webhooks/{id}Unsubscribe

    Add an item in plain words, the way quick add reads them, or field by field:

    curl -X POST https://www.usemadi.com/api/v1/items \
      -H "Authorization: Bearer mad_pat_…" -H "Content-Type: application/json" \
      -d '{"text": "Lab report 2 thu 5pm #chem"}'
    
    curl -X POST https://www.usemadi.com/api/v1/items \
      -H "Authorization: Bearer mad_pat_…" -H "Content-Type: application/json" \
      -d '{"title": "Essay 2", "dueDate": "2026-11-03", "course": "Modern Lit", "kind": "assignment"}'

    Filter items with from, to, course, done, kind and q; sort=created lists the newest first, which is what a polling trigger in Zapier or Make wants. Deleting sends an item to the trash for thirty days, as it does in the planner.

  3. Webhooks

    Register an https address in Settings, or with POST /webhooks (and DELETE /webhooks/{id} to stop, the REST-hooks pattern Zapier uses). Madison posts item.created, item.updated, item.completed, item.deleted whenever an item changes, whether through the API, from an assistant, or in the planner on any device.

    POST /your/endpoint
    Madison-Event: item.completed
    Madison-Delivery: evt_6f1c…
    Madison-Signature: t=1760450000,v1=5e0b…
    
    { "id": "evt_6f1c…", "type": "item.completed", "created": "2026-10-14T15:20:00.000Z",
      "product": "students", "source": "app",
      "data": { "item": { "id": "item_…", "title": "Problem set 4", "dueDate": "2026-10-16", "done": true, … },
                "previous": { … } } }

    The signature is an HMAC-SHA256 of the timestamp, a full stop and the raw body, under the secret shown when you added the webhook. Check it, and refuse anything older than five minutes:

    import { createHmac, timingSafeEqual } from "node:crypto";
    
    function fromMadison(secret, rawBody, header) {
      const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
      if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
      const want = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
      const got = Buffer.from(v1, "hex");
      return got.length === want.length && timingSafeEqual(got, want);
    }

    Answer with any 2xx within ten seconds. Anything else is tried again after a minute, five minutes, half an hour, two hours, eight and a day; deliveries are at least once, so use Madison-Delivery to ignore a repeat. Madison never follows a redirect and only sends to public https addresses. A webhook that fails fifty times running is switched off, and Settings says so.

  4. Madison in Claude

    Madison is a remote MCP server at https://www.usemadi.com/api/mcp. In Claude, open Settings, then Connectors, choose Add custom connector, name it Madison and paste that address. Press Connect: Claude sends you to Madison, you sign in if you need to, choose the planner and press Allow. That is all; there is no token to copy. Then ask “what’s due this week?”, “move the lab report to Friday” or “plan my week”.

    In Claude Code, or anything that takes a command line:

    claude mcp add --transport http madison https://www.usemadi.com/api/mcp
    
    # or with a token instead of signing in
    claude mcp add --transport http madison https://www.usemadi.com/api/mcp \
      --header "Authorization: Bearer mad_pat_…"

    What an assistant can do with it:

    • get_today: What is due today, what is overdue, the next thing due, today's planned study sessions and the fixed parts of the day.
    • list_upcoming: Open items due in the next few days, soonest first, optionally for one course.
    • search_items: Find items in any term by words in their title, notes, course or type.
    • grades_summary: Each course's grade so far (percent and letter), how much of it is settled, the target, and what is needed on the rest to reach it.
    • add_item: Add a dated item to a course. Either give title and due_date (and course when there is more than one), or give text in quick-add style, e.g. "Lab report 2 thu 5pm #chem", and Madison reads the date, time, type and course from it.
    • complete_item: Mark an item done (or not done again).
    • move_item: Give an item a new due date, and optionally a new time.
    • plan_week: Lay out study sessions for a week around the fixed commitments and deadlines, with Madison's planner, and save it as that week's plan (replacing any plan the week had).
    • log_study_session: Record time actually spent studying, so the plan and the week's numbers learn from it.

    Each of your terms is also offered as a resource the assistant can read whole. A connection allowed as read-only sees only the tools that read. Disconnect it in Settings, under Developers, and every token it was given stops at once.

  5. Madison in ChatGPT

    In ChatGPT, open Settings, then Apps & Connectors, and under Advanced turn on Developer mode. Choose Create, name it Madison, paste https://www.usemadi.com/api/mcp, choose OAuth for authentication, and connect: you are sent to Madison to sign in and press Allow, as with Claude. Madison then appears in the tools of a new chat. The search and fetch tools that ChatGPT’s deep research looks for are there too, over your items.

  6. Shortcuts on iPhone

    Recipes, each a few actions in the Shortcuts app. Make a token in Settings first, and put it in a Text action at the top of each shortcut. Every request uses Get Contents of URL with a header Authorization set to Bearer followed by your token, and X-Timezone set to your zone (Europe/London, America/New_York). Name a shortcut what you will say to Siri.

    Add to Madison. Ask for Input (“What’s due?”), then Get Contents of URL: https://www.usemadi.com/api/v1/items, method POST, Request Body JSON with one key, text, set to the input. Then Get Dictionary Value data.title and Show Notification. Say “Lab report thursday 5pm chem” to Siri and it is in.

    What’s next. Get Contents of URL: https://www.usemadi.com/api/v1/today. Get Dictionary Value data.next.title and data.next.dueDate, then Speak Text: “Next is … , due …”. Add Repeat with Each over data.due to hear everything due today.

    Today’s agenda, as a notification. Get Contents of URL: https://www.usemadi.com/api/v1/snapshot. Get Dictionary Value data.today.items; Repeat with Each item, Get Dictionary Value title and add it to a variable with Add to Variable; after the repeat, Combine Text with new lines and Show Notification, titled with data.today.dueCount “due today”. Add data.cushion.warning as a second line: it is empty unless a crunch is coming. In the Automation tab, run it every weekday at 7:30 with Run Immediately on.

    Log study time. Ask for Input (Number, “How many minutes?”). Get Contents of URL https://www.usemadi.com/api/v1/courses, Get Dictionary Value data, then Choose from List over the names (Repeat with Each, name). Get Contents of URL: https://www.usemadi.com/api/v1/sessions, POST, JSON {"minutes": <input>, "course": <chosen>}. It counts towards the course’s time on the overview and teaches the planner how long its work really takes.

    Add an exam. Ask for Input (Text, “Which exam?”), Ask for Input (Date and Time, “When?”), and choose the course as above. Format Date the date as Custom yyyy-MM-dd and again as HH:mm. Get Contents of URL: https://www.usemadi.com/api/v1/items, POST, JSON {"title": <exam>, "kind": "exam", "dueDate": <date>, "dueTime": <time>, "course": <chosen>}. It joins the exam countdown on your widgets.

    Start and stop focus. Get Contents of URL: https://www.usemadi.com/api/v1/timer, POST, JSON {"action": "start", "minutes": 50}; it starts on the next thing due unless you add itemId. The planner, the menu bar app and the browser extension’s site blocker all see it at once. Add Set Focus (a Study focus) on if you like. A second shortcut posts {"action": "stop"}, which logs the time and answers with data.minutes; turn the Focus off after it.

  7. Widgets, the menu bar and the browser

    Madison’s own widgets, menu bar app and browser extension use this API and nothing else, so anything they do, yours can. Three endpoints are made for them:

    • GET /snapshot: today’s list, the next thing due, the next exam, this week’s time cushion and the focus timer, in one answer. It sends an ETag; ask again with If-None-Match and an unchanged day is a bodiless 304, so a widget can ask every minute for almost nothing.
    • GET and POST /timer: the focus timer, shared by every device. start, pause, resume and stop; stopping logs the session. While it runs, startedAt and elapsedSec let a clock count on by itself.
    • POST /capture: add from a page. A line with a date becomes an item at once; a paragraph or a page is read the way forwarded email is, and what is found waits on the overview to be accepted.
    curl https://www.usemadi.com/api/v1/snapshot -H "Authorization: Bearer mad_pat_…" -i
    # HTTP/1.1 200 OK
    # ETag: "s1-…"
    
    curl https://www.usemadi.com/api/v1/snapshot -H "Authorization: Bearer mad_pat_…" -H 'If-None-Match: "s1-…"'
    # HTTP/1.1 304 Not Modified

    Native apps sign in with OAuth 2.1 and PKCE, registering themselves first (dynamic client registration at /api/oauth/register); a browser extension’s chromiumapp.org address, a loopback address and an app’s own scheme are all accepted redirects. Everything there is to install is on Get the apps.

  8. Forward mail to Madison

    Where the deployment has it switched on, Settings gives you a private address. Forward a teacher’s email, a changed schedule or an invitation to it, and the dated things in it are read out (by Claude, or by Madison’s own rules where there is no key) and wait on your overview under From your inbox to be accepted, edited or dismissed. Nothing reaches your plan until you say so. A new address stops the old one.

  9. What we promise

    Version 1 will not change under you: new fields and endpoints may appear, nothing is renamed or removed without a new version and notice in the changelog. Tokens and codes are stored as hashes, webhook secrets sealed. Nothing you do through the API is shared with anybody else. Questions, or something you wish it did: hello@madisonplanner.app.