Page data
Every DocPin page has a data store: one JSON object, up to 1 MB, kept separately from the page's HTML. The page reads it in the browser. You change it from the API, from an AI app over MCP, or in the DocPin web app, and the page shows the new values without being republished.
Use it for anything that changes more often than the page itself: dashboards, status boards, price lists, scores, schedules, "last updated" banners.
How it fits together
your script / cron / CI ──PATCH /v1/pages/{id}/data──┐
Claude or ChatGPT ───────set_page_data (MCP)─────────┤
DocPin web app ──────────Data tab────────────────────┤
▼
page data (JSON, ≤ 1 MB)
│
docpin.data() / docpin.onData() ◀┘ (in the published page)- Updating data doesn't create a version, and the page's version history isn't touched.
- Data belongs to the page, not to a version: publishing a new version, or restoring an old one, keeps the current data.
- Deleting the page deletes its data.
- Available on Free and Pro.
Read it in the page
DocPin adds a small script to every published page, which provides a global docpin object. You don't include anything yourself.
docpin.data()
Returns a promise for the page's data object ({} if nothing has been set).
<div id="revenue">…</div>
<script>
docpin.data().then((data) => {
document.getElementById("revenue").textContent =
data.revenue == null ? "No data yet" : "$" + data.revenue.toLocaleString();
});
</script>After it resolves, docpin.updatedAt holds when the data last changed (ISO 8601), or null.
docpin.onData(callback, { every })
Reads the data now, then re-checks every every seconds (default 30, minimum 10) and calls callback(data) whenever it changed. Checks pause while the tab is hidden. It returns a function that stops checking.
const stop = docpin.onData((data) => render(data), { every: 60 });
// later, if you need to: stop();Use onData for anything left open on a screen, like a TV dashboard: new data appears without anyone reloading.
Writing pages that use data
- Render from data, not from hard-coded numbers. Keep the layout in the HTML and the values in the data store, so updates never need a republish.
- Always handle empty data. A new page has
{}. Show a clear empty state ("No data yet") rather thanundefinedorNaN. - Treat every field as optional. Someone may update one key and not another, so check each value before using it.
- Keep the shape flat and named. Prefer
{ "revenue": 128400, "regions": [{ "name": "NZ", "value": 40 }] }over deeply nested or positional data. - Include your own timestamp if it matters to readers, e.g.
"asOf": "2026-11-06T09:00:00Z".docpin.updatedAtis when the data was stored, which may differ from when the numbers were measured.
Update it from code: the REST API
Base URL: https://api.docpin.app. Authenticate with a workspace API key: Authorization: Bearer dp_…. Workspace owners create keys in the DocPin app under Connections → API keys. You need the page's id, which GET /v1/pages lists and every publish response includes as doc.id.
| Method | Path | What it does |
|---|---|---|
GET | /v1/pages/{id}/data | Returns { "data": {…}, "updatedAt": "…" | null } |
PATCH | /v1/pages/{id}/data | Merges the top-level keys you send. A null value removes that key. |
PUT | /v1/pages/{id}/data | Replaces all of the page's data with the object you send. |
The request body is the JSON object itself, not wrapped. Both writes return the full stored data and its new updatedAt.
curl -X PATCH https://api.docpin.app/v1/pages/$PAGE_ID/data \
-H "Authorization: Bearer $DOCPIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"revenue": 128400, "asOf": "2026-11-06T09:00:00Z", "stale": null}'{ "data": { "revenue": 128400, "asOf": "2026-11-06T09:00:00Z" }, "updatedAt": "2026-11-06T09:00:02.114Z" }Prefer PATCH. It only touches the keys you send, so two jobs that each own different keys (say, sales and support numbers) can't overwrite each other. Use PUT when one job owns the whole object.
On a schedule
Any scheduler that can make an HTTPS request works: cron, GitHub Actions, a serverless function, Zapier or Make. For example, a GitHub Actions workflow:
on:
schedule: [{ cron: "*/15 * * * *" }]
jobs:
update:
runs-on: ubuntu-latest
steps:
- run: |
REVENUE=$(curl -s https://example.com/internal/revenue) # your source
curl -fsS -X PATCH https://api.docpin.app/v1/pages/${{ vars.PAGE_ID }}/data \
-H "Authorization: Bearer ${{ secrets.DOCPIN_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{\"revenue\": $REVENUE, \"asOf\": \"$(date -u +%FT%TZ)\"}"Keep the API key in your scheduler's secrets. It can publish and change every page in the workspace.
Update it from an AI app: MCP
With DocPin connected to Claude or ChatGPT (see MCP), two tools work with page data:
| Tool | Arguments | What it does |
|---|---|---|
get_page_data | slug (and board if in one), or id | Returns the page's current data and when it changed. |
set_page_data | the page, data (object), optional replace: true | Merges data in (a null removes a key), or replaces everything with replace: true. |
People can ask in plain language: "update the sales dashboard with these numbers", "clear the old regions from the dashboard data". An AI app should call get_page_data before replacing data, so it doesn't drop keys it didn't know about.
Scheduled AI tasks (for example a daily Claude task that pulls figures from a connected tool) can call set_page_data the same way.
Edit it by hand
In the DocPin web app, open the page and choose the Data tab. Edit the JSON and choose Save data. Only the keys you changed are sent, so a scheduled update made while you were editing isn't undone.
Limits
| Limit | Value |
|---|---|
| Size | 1 MB per page, measured as JSON text. Larger writes get 413. |
| Shape | The top level must be a JSON object. Values can be any JSON. |
| Keys | 1 to 200 characters. |
| Writes | About 30 a minute per workspace. Over that you get 429; wait a minute. |
| Checks from pages | docpin.onData checks at most every 10 seconds per open page. |
Who can see it
Page data is exactly as visible as the page. It's served from the page's own address:
- On a public page, anyone with the link can read the data.
- On a password page, only people who entered the password can read it.
- On a people-only page, only the people allowed in can read it.
When you change a page's password or people list, links that people opened before the change stop working for the page and its data until they open the page again.
Version previews never show the current data.
Never put secrets in page data: no API keys, passwords, tokens or personal information you wouldn't put on the page itself. If a page needs data from a private source, have your server or scheduled job fetch it and push only the results.
Errors
Errors come back as { "error": "Plain-English message" } with these statuses:
| Status | Meaning |
|---|---|
400 | The body isn't a JSON object, or a key is empty or too long. |
401 | Missing or invalid API key, or the person who created the key left the workspace. |
403 | DocPin took the page down. |
404 | No such page in this workspace (check the id). |
409 | The data changed several times while saving. Retry. |
413 | Over 1 MB. |
429 | Too many writes. Wait a minute. |
Related
- Live dashboard recipe: a complete page and update script to copy.
- How pages run: links, URL state and browser storage.
- REST API and MCP references.