#NotesApp
Docs

#NotesApp API

Server-to-server access for Enterprise partners: publish posts, and read your own bookings, orders and earnings. Get access by contacting us, then create keys in the Console. Changes are listed in the Changelog and announced on our Discord, where you can ask the team and other developers about the API and the MCP server.

Quickstart

  1. Ask us to switch on API access for your account with the contact form.
  2. Open the Console and create a key with the permissions you need. Copy it — it's shown once.
  3. Call the API:
curl https://www.naapp.tech/api/v1/me \
  -H "Authorization: Bearer nak_your_key_here"

Authentication

Send your key as a bearer token on every request. Keys look like nak_<id>.<secret>, belong to one account and carry scopes: read:posts, write:posts, read:bookings, read:orders, read:earnings. We store only a hash of the secret. Revoke a key in the Console and it stops working at once. Keep keys on your server — never in a browser or mobile app.

Errors & limits

Errors share one shape and the usual HTTP status:

{ "error": { "code": "missing_scope", "message": "This key doesn't have the `write:posts` scope." } }

Endpoints

Base URL: https://www.naapp.tech/api

GET/v1/mescope: any key

The account a key belongs to — a quick way to test your key.

{ "id": "uid_123", "username": "precheks", "display_name": "Precheks", "plan": "enterprise",
  "profile_url": "https://www.naapp.tech/u/precheks", "payouts_ready": true }
GET/v1/postsscope: read:posts

Your posts, newest first.

  • status (query) — `draft` or `published`.
  • limit (query) — 1–100, default 25.
  • cursor (query) — `next_cursor` from the previous page.
{ "data": [ { "id": "n_1", "slug": "minding-the-pay-gap", "url": "https://www.naapp.tech/journals/minding-the-pay-gap",
    "title": "Minding the Pay Gap", "status": "published", "date": "2026-10-03T09:00:00.000Z",
    "tags": [], "categories": [], "featured_image": "", "premium": false,
    "view_count": 8, "like_count": 1, "share_count": 0 } ], "next_cursor": null }
GET/v1/posts/{id}scope: read:posts

One of your posts, including its Markdown `content`.

{ "id": "n_1", "title": "Minding the Pay Gap", "status": "published", "content": "# …", "…": "…" }
POST/v1/postsscope: write:posts

Create a post as the account. Drafts unless you send `status: "published"`. Send an `Idempotency-Key` header to make retries safe.

  • title (body, required) — Up to 140 characters.
  • content (body, required) — Markdown, up to 100,000 characters.
  • status (body) — `draft` (default) or `published`.
  • tags (body) — Up to 10 strings.
  • categories (body) — Up to 5 strings.
  • featured_image (body) — An https image URL.
  • premium (body) — `true` to lock it behind a subscription.
// 201 Created
{ "id": "n_2", "slug": "hello-world", "status": "draft", "title": "Hello world", "content": "…", "…": "…" }
PATCH/v1/posts/{id}scope: write:posts

Edit your own post, or publish / unpublish it with `status`. Send only the fields you want to change.

{ "id": "n_2", "status": "published", "…": "…" }
GET/v1/bookingsscope: read:bookings

Sessions booked with you. Client contact details are never returned.

  • status (query) — e.g. `confirmed`.
  • limit (query) — 1–100.
  • cursor (query) — Pagination cursor.
{ "data": [ { "id": "ref_1", "date": "2026-10-12", "slot": "10:00", "minutes": 60,
    "starts_at": "2026-10-12T09:00:00.000Z", "status": "confirmed", "amount_kobo": 1500000, "created_at": "…" } ], "next_cursor": null }
GET/v1/ordersscope: read:orders

Store orders for your physical items. Add `kind=digital` for download sales. Buyers' names, phones and street addresses are never returned — only city and state.

  • kind (query) — `digital` for download sales (default is physical orders).
  • status (query) — Physical orders only, e.g. `paid`.
{ "data": [ { "id": "ref_2", "item_title": "Tote bag", "quantity": 2, "amount_kobo": 1300000,
    "commission_kobo": 52000, "status": "paid", "parcel_id": "NA-…", "ship_to": { "city": "Abuja", "state": "FCT" }, "paid_at": "…" } ], "next_cursor": null }
GET/v1/earningsscope: read:earnings

Your payout ledger and a summary by status (held, transferring, paid …). Amounts are in kobo (₦1 = 100 kobo).

  • kind (query) — `booking`, `order`, `digital`, `gift` or `subscription`.
{ "summary": { "held": { "count": 3, "net_kobo": 4200000 }, "paid": { "count": 9, "net_kobo": 15000000 } },
  "data": [ { "id": "ref_1", "kind": "booking", "gross_kobo": 1500000, "commission_kobo": 225000, "net_kobo": 1275000,
    "status": "held", "release_after": "…", "created_at": "…" } ], "next_cursor": null }

Webhooks

Add an https endpoint in the Console and choose events. We POST JSON, wait up to 5 seconds for a 2xx, and log every delivery (resend any of them from the Console).

{ "id": "evt_9f2c…", "type": "order.paid", "created": 1791100800,
  "data": { "id": "ref_2", "item_title": "Tote bag", "quantity": 2, "amount_kobo": 1300000 } }

Verify the signature. Each request carries NotesApp-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 of "<t>.<raw body>" using your endpoint's signing secret. Reject anything that doesn't match or whose timestamp is more than five minutes old.

import { createHmac, timingSafeEqual } from "crypto";

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

MCP server for AI assistants

AI assistants that speak the Model Context Protocol (Claude, ChatGPT and others) can use #NotesApp through our MCP server: find creators, journals, store items and courses, see a creator's open session times, and hand back booking links. It only reads. Booking, buying and paying always happen on #NotesApp itself. Add it to your assistant as a remote (Streamable HTTP) server at:

https://mcp.naapp.tech

It answers at https://www.naapp.tech/api/mcp too; use either. No sign-in is needed for the public tools.

The my_* tools read your own account through the same API as above: send your API key as Authorization: Bearer nak_…(the key's scopes apply). Every tool call is logged for 12 months (the tool, the arguments and the key's id, never the secret). Text members wrote, such as bios and titles, is marked as data so assistants don't take it as instructions. Up to 120 calls a minute.

search_creators

Find #NotesApp creators, professionals and organisations by name, @username, job, industry or bio. Set bookable to only list people who take paid 1:1 sessions.

get_creator

A creator's public profile: who they are, their links, whether they take paid sessions (length and price) and their latest journals.

get_open_slots

The open times for a paid 1:1 session with a creator over the next days (Lagos time, WAT), each with a booking link. The person books and pays on #NotesApp.

get_booking_link

The link where someone books (and pays for) a session with a creator; with a date and time, the booking card opens on that slot.

search_store

Items for sale on Notes Market (physical items and downloads), with price, seller and link.

search_journals

Published journals (articles) on #NotesApp by words and topic, newest first, with author, topics, an excerpt and link.

search_courses

Free and paid courses on Notes Academy, with teacher, price, level, lessons, length and link.

my_profileAPI KEY

Your own #NotesApp account: username, plan, profile link, whether payouts are set up. Needs your API key.

my_postsAPI KEY

Your journals, newest first; status draft or published. Needs your API key with read:posts.

my_bookingsAPI KEY

Sessions booked with you, newest first (no client contact details). Needs your API key with read:bookings.

my_ordersAPI KEY

Orders for your store items (kind digital lists download sales); delivery city and state only. Needs your API key with read:orders.

my_earningsAPI KEY

Your payout ledger with a summary of what is held, in transit and paid (amounts in kobo). Needs your API key with read:earnings.

Try it with curl:

curl https://www.naapp.tech/api/mcp   -H "Content-Type: application/json"   -H "Accept: application/json, text/event-stream"   -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"search_creators","arguments":{"query":"therapist","bookable":true}}}'

Coming next: signing in with your #NotesApp account instead of a key, and tools that can draft posts and reply to bookings, always with your approval.

Building something, or stuck? Ask on our Discord.

Your own domain

Business and Enterprise accounts get their own branded site on their own domain — notes.yourbrand.com or the root yourbrand.com. Add it in the Console, create the DNS record it shows (a CNAME for a subdomain, an A record for a root domain), then press Check status. If your domain isn't hosted anywhere yet, or your provider won't accept the record, the easiest route is to set the domain's nameservers to ns1.vercel-dns.com and ns2.vercel-dns.com at your registrar, so Vercel manages the records (this moves all of the domain's DNS, including any email records). The site has your name in the header, three pages — Home (your profile and booking), Notes (/notes) and Shop (/shop) — and a small “powered by #NotesApp” footer. Your /u/username page keeps working; you choose whether the front page shows Home or your Shop. Visitors browse, sign in, book sessions and buy right on your domain. Signing in briefly passes through www.naapp.tech (where accounts live) and brings them straight back, already signed in; payments run on Paystack and return to your site.