Documentation

Build and understand your OneNote System

Start with the beginner deployment path, then use this page as the reference for Microsoft permissions, settings, the OneNote APIs, privacy, updates, and common errors.

Start here: what you are creating

You are not creating an account on a shared OneNote System service. You are making your own copy of the project in accounts you control:

PartWhat it doesWho controls it
GitHub repositoryHolds the open-source code and sends updates to VercelYou
Vercel projectRuns the website and private capture API on the internetYou
Neon databaseStores encrypted Microsoft credentials, your chosen section, and hashed API keysYou
Microsoft app registrationLets your deployment ask Microsoft for limited, revocable OneNote accessYou
ShortcutSends title and text from your device to your deploymentYou

Expect the first setup to take roughly 20–40 minutes because both Vercel and Microsoft require several confirmation screens. You do not need to write code, but you do need to copy values exactly.

Part 1: deploy your copy to Vercel

What Vercel is: Vercel runs the OneNote System website and API for you. When a Shortcut sends a note, Vercel receives the request and runs the code. Your personal computer does not need to stay on.

  1. Create or sign in to GitHub

    Use GitHub's sign-up page ↗. GitHub stores your copy of the source code. Keep the repository private if you ever add personal changes, and never commit secret values.

  2. Open the deployment link

    Deploy OneNote System on Vercel ↗

    Vercel will ask you to sign in. Choosing Continue with GitHub is easiest because Vercel needs permission to create and deploy your copy.

  3. Create the Git repository

    On the “Create Git Repository” screen, keep the name onenote-system or choose another name. This new repository belongs to your GitHub account; it is not the original project.

  4. Create the first deployment

    Choose Deploy. The first deployment may show setup errors because the database and private settings do not exist yet. That is expected. Wait until Vercel gives you a project URL ending in .vercel.app, then copy that complete URL.

Vercel can automatically deploy later changes pushed to the connected GitHub repository. See Vercel's official Git deployment documentation ↗ for how that relationship works.

Part 2: add a Neon database

What Neon is: Neon is a company that runs PostgreSQL databases. PostgreSQL is the database software; Neon operates it, secures the database server, and gives your Vercel project a private connection address. For light personal use, its free tier is usually enough, subject to Neon's current limits.

Why this is required: Vercel functions do not keep permanent files between requests. The database remembers your encrypted Microsoft connection, default section, and API keys after each request ends.

  1. Open Neon inside the Vercel Marketplace

    Visit the official Neon Marketplace listing ↗ while signed in to Vercel.

  2. Add the integration

    Choose Add Integration. Select your Vercel account and the OneNote System project you created above. Continue to Neon and create an account if asked.

  3. Create the database

    Use a recognizable project name such as onenote-system. Use the region closest to you. Keep the generated database and role defaults unless you know you need something different.

  4. Confirm the project connection

    Neon/Vercel should add an environment variable named DATABASE_URL to the OneNote System project. You do not need to open or copy its value.

  5. Redeploy

    In Vercel, open your OneNote System project, then Deployments. Open the ⋯ menu beside the newest deployment and choose Redeploy. Existing deployments cannot see settings added afterward.

Never share DATABASE_URL. Despite its name, it contains the database hostname, username, and password. Neon's official manual Vercel connection guide ↗ is available if the Marketplace integration does not add it automatically.

Part 3: add the private application settings

An environment variable is a private setting Vercel gives to the running app without placing it in GitHub. Open your Vercel project, then Settings → Environment Variables.

NameRequired?What to enter
APP_ENCRYPTION_KEYYesA new random secret used to encrypt Microsoft credentials and sign login cookies. Generate it once and do not change it casually.
APP_URLYesYour complete production URL, such as https://onenote-system-abc.vercel.app, with no trailing slash.
DATABASE_URLYesNeon should add this automatically. Do not replace it with the words shown in the example.
CRON_SECRETNoA separate random secret. The current maintenance endpoint performs no synchronization work, so this can be omitted.

Generate the encryption key

If you have a Mac, open Terminal, run openssl rand -base64 32, and copy the result. On Windows, use a trusted password manager's password generator to create at least 43 random letters, numbers, and symbols. Do not use a normal password, your name, or the example text.

# Required: generate a new random value for each installation
APP_ENCRYPTION_KEY=PASTE_A_RANDOM_32_BYTE_KEY

# Required: the public address of this installation, with no trailing slash
APP_URL=https://YOUR-PROJECT.vercel.app

# Required: added automatically by the Vercel + Neon integration
DATABASE_URL=postgresql://...

# Optional: protects the currently empty maintenance cron endpoint
CRON_SECRET=PASTE_ANOTHER_LONG_RANDOM_VALUE

Apply each variable to Production, Preview, and Development if Vercel asks. Save, then redeploy again. Vercel documents these settings in its environment variable guide ↗.

Part 4: create your Microsoft app registration

What you are doing: Microsoft will not let an unknown website read or write OneNote. An app registration gives your deployment an identity, a list of permitted actions, and a callback address. You can remove the registration later to revoke the connection.

Keep your Vercel project URL nearby. The callback address must match exactly. A missing letter, extra slash, or http instead of https will stop sign-in.

  1. Open Microsoft Entra

    Go to the Microsoft Entra admin center ↗ and sign in with the Microsoft account that owns the OneNote notebooks. Personal Microsoft accounts may be redirected through an account/tenant setup screen.

  2. Start an app registration

    Open Identity → Applications → App registrations, then choose New registration. Microsoft's official app registration guide ↗ includes screenshots and definitions.

  3. Name it and choose account access

    Name it OneNote System. Choose Accounts in any organizational directory and personal Microsoft accounts if you want the deployment to accept either work/school or personal Microsoft accounts.

  4. Add the Web redirect URI

    For platform, choose Web. Enter https://YOUR-VERCEL-DOMAIN/api/auth/microsoft/callback. Replace only YOUR-VERCEL-DOMAIN. Example: https://my-queue.vercel.app/api/auth/microsoft/callback.

  5. Copy the Application (client) ID

    After registration, the Overview screen shows an Application (client) ID. Copy the long identifier. Do not copy the Object ID or Directory ID.

  6. Create a client secret

    Open Certificates & secrets → Client secrets → New client secret. Name it OneNote System, choose an expiration, and create it. Copy the secret's Value immediately. Do not copy the Secret ID. Microsoft will never display the Value again.

  7. Add Microsoft Graph permissions

    Open API permissions → Add a permission → Microsoft Graph → Delegated permissions. Add User.Read, Notes.ReadWrite, and Tasks.ReadWrite. offline_access is requested during Microsoft sign-in so the connection can refresh without asking you every hour.

    Tasks.ReadWrite is what powers Microsoft To Do. Leave it out if you only want OneNote pages — everything else still works. If you add it to an existing deployment, reconnect Microsoft afterwards: a permission added later does not upgrade a connection that was already approved.

Microsoft's Graph documentation confirms that creating OneNote pages uses delegated OneNote permissions and that page content is sent as HTML. See Create OneNote pages ↗.

Part 5: connect Microsoft and select a section

  1. Open your deployment's setup page

    Use https://YOUR-VERCEL-DOMAIN/setup. Do this on your deployed copy, not this project's public information site.

  2. Save the Microsoft app values

    In Setup Step 2, paste the Application (client) ID and the client secret Value. The server encrypts the secret before storing it.

  3. Connect your account

    Setup Step 3 sends you to Microsoft. Verify that the browser address is a Microsoft domain, read the requested access, and approve it.

  4. Choose the destination

    In Setup Step 4, load notebooks, choose a notebook, and select the section that should behave as your capture inbox.

  5. Create a Shortcut API key

    Setup Step 5 displays the key exactly once. Save it in your password manager. The database stores only a hash and cannot show the original later.

Part 6: install or build a Shortcut

The Shortcut needs two values: your deployment URL and the ons_ API key from Setup Step 5. It does not need your Microsoft client secret, Microsoft password, database URL, or encryption key.

Open the complete Shortcuts guide →

The Shortcuts page identifies which iCloud download links are still missing, explains every configuration answer, and includes a complete manual Quick Inbox build that works without a download.

Part 7: use OneNote from a terminal

The onenotesystem command is the official command-line client. It takes the same two values a Shortcut does — your deployment URL and an ons_ API key — and calls the same two endpoints, so pages created from a shell and pages created from a phone are identical.

npm install -g onenotesystem onenotesystem configure onenotesystem capture "Standup notes" --content "Shipped the CLI"

It exists for the machines OneNote will not run on: Linux desktops, servers you only reach over SSH, and work computers where the OneNote app cannot be installed. It needs Node.js 18.17 or newer and has no other dependencies.

Open the complete terminal guide →

OneNote API reference

The API can create a page in the signed-in user's default section or append plain text to an existing page.

MethodPathAuthenticationSuccess
POST/api/captureAuthorization: Bearer ons_…201 Created
POST/api/appendAuthorization: Bearer ons_…200 OK

Create a page: POST /api/capture

JSON fieldRequired?Limit and behavior
titleNoPlain text, trimmed to 200 characters. Defaults to “Untitled capture.”
contentNoPlain text, up to 100,000 characters. New lines become line breaks.
urlNoIncluded as a source link only when it begins with http:// or https://.

Example request

curl -X POST "https://YOUR-DOMAIN/api/capture" \
  -H "Authorization: Bearer ons_YOUR_PRIVATE_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "title": "Saved from OneNote System",
    "content": "This becomes the body of a new OneNote page.",
    "url": "https://example.com/source"
  }'

Example success response

{
  "ok": true,
  "page": {
    "id": "…",
    "title": "Saved from OneNote System",
    "webUrl": "https://…"
  }
}

Append to a page: POST /api/append

JSON fieldRequired?Limit and behavior
pageTitleOne target requiredExact title of a page in the default section, up to 200 characters. Duplicate titles return 409.
pageIdOne target requiredMicrosoft Graph page ID. Use this instead of pageTitle to identify a page exactly or target another section.
contentYesNon-empty plain text, up to 100,000 characters. New lines become line breaks.
urlNoAppended as a source link only when it begins with http:// or https://.

Example append request

curl -X POST "https://YOUR-DOMAIN/api/append" \
  -H "Authorization: Bearer ons_YOUR_PRIVATE_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "pageTitle": "Quick Inbox",
    "content": "This text is added to the existing page.",
    "url": "https://example.com/source"
  }'

Example append response

{
  "ok": true,
  "page": {
    "id": "…",
    "title": "Quick Inbox",
    "webUrl": "https://…"
  }
}

Error responses use { "error": "Explanation" }. A 400 means the JSON or fields are invalid; 401 means the API key is missing/incorrect; 404 means the requested page title was not found; 409 means a default section is missing or a title is ambiguous; 500 means the database or Microsoft request failed.

Microsoft To Do

Available when the connection has approved Tasks.ReadWrite. All four use the same ons_ API key as the OneNote endpoints.

RequestDoes
GET /api/todo/listsYour To Do lists, default first
GET /api/todoOpen tasks. ?list=, ?all=true, ?top=
POST /api/todoCreate a task from title, and optional note, list, dueDate, reminder, timeZone
POST /api/todo/completeMark a task done by id

Send timeZone as an IANA name such as America/Chicago. A bare dueDate of 2026-09-15 is anchored to midnight in that zone, so the task stays on the day you meant rather than shifting for anyone west of UTC.

Security and data storage

InformationWhere it is storedProtection
Setup step numberYour browser's localStorageNot sensitive; used only to reopen the last viewed step
Light/dark preferenceYour browser's localStorageNot sensitive; used to restore the selected theme before the page appears
Login sessionHttpOnly cookieSigned; JavaScript cannot read it; Secure in production
Microsoft client secretYour databaseAES-256-GCM encrypted using APP_ENCRYPTION_KEY
Microsoft access/refresh tokensYour databaseAES-256-GCM encrypted
Discord bot tokenYour databaseAES-256-GCM encrypted; never returned to the browser after saving
Discord public key and application IDsYour databaseNot secrets; used to verify and route signed interactions
Shortcut API keysYour databaseOne-way SHA-256 hash; original shown once
Captured note textSent to Microsoft during the requestNot intentionally retained by the OneNote System database

Keep APP_ENCRYPTION_KEY, DATABASE_URL, Microsoft client secrets, refresh tokens, API keys, and Discord bot tokens out of GitHub and screenshots. If an API key leaks, create a replacement. Key revocation UI is not yet included, so database/manual administration is currently required to remove an old key.

This software is provided under the MIT License without warranty. Review the source and provider terms before using it for sensitive or regulated information.

Self-host with Docker

This route is for people comfortable maintaining a Linux server. You need Docker, Docker Compose, a domain name, HTTPS, backups, and a plan for applying updates.

  1. Clone the repository

    git clone https://github.com/DudeThatsErin/onenote-system.git
    cd onenote-system
    cp .env.example .env
    # Edit .env before continuing.
    docker compose up -d --build

    Do not run the final command until the environment file and database password are changed.

  2. Set a strong PostgreSQL password

    Edit docker-compose.yml or supply the documented Compose variable. The password in the example is not safe for a public server.

  3. Complete .env

    Set APP_URL to your HTTPS domain, generate APP_ENCRYPTION_KEY, and set DATABASE_URL to the PostgreSQL container connection string.

  4. Start the containers

    Run docker compose up -d --build, inspect docker compose logs -f app, and verify https://YOUR-DOMAIN/api/health.

  5. Put it behind HTTPS

    Use Nginx, Caddy, Traefik, or another reverse proxy. Microsoft OAuth requires an exact, stable HTTPS callback address in production.

  6. Back up PostgreSQL

    The database contains the encrypted connection and configuration. Losing the database means reconnecting accounts and generating new API keys.

Update an installation

Vercel

Your deployed GitHub repository is a separate copy. GitHub does not automatically pull changes from the original project. Use GitHub's “Sync fork” feature if your repository is a fork, review changes, then let Vercel deploy the updated branch. Back up the database before a release that changes its schema.

Self-hosted

git pull --ff-only
docker compose build --pull
docker compose up -d
docker compose logs --tail=100 app

Read release notes first and preserve your .env and PostgreSQL volume.

Discord integration

Setup Step 6 connects a Discord application to the same Microsoft account and default OneNote section used by this installation. It registers one /onenote command with two actions:

  • /onenote create creates a page from a title, optional text, and optional source URL.
  • /onenote append adds text and an optional source URL to an exact page title in the default section.

Discord application setup

  1. Create the application

    Open the Discord Developer Portal ↗, create an application, and copy the Application ID and Public Key from General Information.

  2. Create the bot token

    Open Bot, reset the token, and save it in Setup Step 6. OneNote System encrypts it. The token is used only when registering or removing the slash command; interaction responses use Discord's signed webhook flow.

  3. Save the interaction endpoint

    Save the Discord fields in OneNote System first. Copy the exact URL ending in /api/discord/interactions into General Information → Interactions Endpoint URL. Discord sends a signed PING and the endpoint returns PONG during validation.

  4. List who may use the command

    Enter your own Discord user ID in Allowed Discord user IDs, and anyone else you trust, separated by commas. The command writes into your OneNote, so seeing it in a server is not permission to use it — only the accounts listed here can run it. Leaving the field blank allows nobody. To find your ID, turn on Developer Mode in Discord's Advanced settings, right-click your name, and choose Copy User ID.

  5. Install and register the command

    Use the installation link to authorize the applications.commands scope. Then choose Save and register /onenote. A test server ID creates a guild command that updates immediately; leaving it blank creates a global command.

  6. Test both actions

    Run /onenote create, then run /onenote append with that page's exact title. Discord shows a private result and links to the OneNote page when Microsoft supplies a web URL.

The endpoint validates the raw request body with Discord's Ed25519 signature headers, acknowledges commands within Discord's three-second window, completes Microsoft Graph work in the background, and edits the private response afterward. See Discord's official interaction security guide ↗ and application command reference ↗.

Every Discord account on your allowed list can write to your selected OneNote section. Keep that list as short as you actually need, and remove people when they no longer need access. If the bot token leaks, reset it in Discord and save the replacement in Setup Step 6.

Troubleshooting

Setup says DATABASE_URL is not configured

The Neon integration was not connected to this Vercel project, or the app was not redeployed after it was connected. Check Vercel → Project → Settings → Environment Variables for DATABASE_URL, then redeploy.

Microsoft says the redirect URI does not match

Compare the URI in Entra with APP_URL plus /api/auth/microsoft/callback. They must match exactly, including https, subdomain, path, and absence of a trailing slash.

To Do commands say access has not been approved

The connection was approved before Tasks.ReadWrite was added. Add that delegated permission in your Microsoft app registration, then reconnect Microsoft in setup. OneNote keeps working the whole time.

Microsoft sign-in works but notebooks do not load

Confirm that the app registration has delegated Notes.ReadWrite permission and that you approved it. Reconnect the Microsoft account after changing permissions.

APP_ENCRYPTION_KEY is not configured

Add a strong random value in Vercel Environment Variables and redeploy. Do not change an existing key after credentials are stored; old encrypted values cannot be decrypted with a new key.

The Shortcut receives Unauthorized

Confirm the header is exactly Authorization: Bearer ons_…. There must be one space after Bearer. The key must come from the same deployment receiving the request.

The Shortcut says no default section is selected

Open Setup Step 4 on the same deployment and choose a notebook and section while signed in with Microsoft.

A secret expired

Create a new Microsoft client secret Value in Entra, save it through Setup Step 2, then reconnect Microsoft. Microsoft recommends rotating credentials and client secrets have limited lifetimes.

Discord rejects the Interactions Endpoint URL

Save the Application ID and Public Key in Setup Step 6 before pasting the endpoint into Discord. Confirm the URL uses HTTPS and ends exactly in /api/discord/interactions. A wrong public key causes the required signature check to return 401.

The /onenote command does not appear

Use a test server ID for immediate testing, confirm the application was installed with the applications.commands scope, and choose Save and register /onenote again. Global command changes can take longer to propagate through Discord.

Where can I report a reproducible bug?

Remove every secret and personal note from screenshots/logs, then open a GitHub issue ↗ with the deployment type, route, status code, and exact non-secret error.