Granite Notes API
Read and change single notes by their text, for scripts and integrations. Each change is an ordinary commit that synced devices, history, and search see.
Read and change single notes by their text, for scripts and integrations. Each change is an ordinary commit that synced devices, history, and search see.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/granite-md:granite-md-notes-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Granite Notes API
version: 1.0.0
summary: REST API for Granite clients, bots, and integrations.
description: 'Conventions (apply to every operation; see PLAN.md 3.2 and 3.6):
- **Base URL** `https://api.granite.md`. All public operations live under `/v1`.
After Phase 6 ships, `/v1` changes are additive only.
- **Errors** use RFC 9457 problem details (`application/problem+json`, schema `Problem`).
The `type` URI is `https://granite.md/problems/`; `code` refines it for programmatic handling.
Every response carries `X-Request-ID`, also echoed as `request_id` in problems.
- **Pagination** is cursor based. List operations accept `cursor` and `limit` and return
`{ "items": [...], "next_cursor": "..." }`. `next_cursor` is absent on the last page.
Cursors are opaque; clients must not parse or build them.
- **Idempotency**: non-idempotent `POST` operations accept an `Idempotency-Key` header
(1-255 printable ASCII). Repeating a request with the same key and body within 24 hours
returns the original response; the same key with a different body returns `409` with
code `idempotency_key_reused`.
- **IDs** are UUIDv7 strings. **Timestamps** are RFC 3339 in UTC.
- **Authentication**: `Authorization: Bearer ` with a session access token,
a personal API token (`gr_pat_...`), or an OAuth access token. Scopes are listed per operation.
- **Rate limits**: each personal API token may make 600 requests a minute. Over the limit
the API answers `429` with code `token_rate_limited` and a `Retry-After` header (seconds).'
license:
name: Proprietary
identifier: LicenseRef-Proprietary
servers:
- url: https://api.granite.md
description: Production
- url: http://localhost:8080
description: Local development
security:
- bearerAuth: []
tags:
- name: Notes
description: 'Read and change single notes by their text, for scripts and integrations. Each change
is an ordinary commit that synced devices, history, and search see.'
paths:
/v1/vaults/{vault_id}/notes:
parameters:
- $ref: '#/components/parameters/VaultID'
get:
operationId: listNotes
tags:
- Notes
summary: List notes
description: 'Live files in path order (all kinds; `GET` on a non-text file answers `not_a_note`).
`folder` limits the list to one folder and its subfolders. Needs `vaults:read`.'
parameters:
- name: folder
in: query
required: false
schema:
type: string
maxLength: 1024
- $ref: '#/components/parameters/Cursor'
- $ref: '#/components/parameters/Limit'
responses:
'200':
description: Notes, without their text.
content:
application/json:
schema:
$ref: '#/components/schemas/NoteList'
default:
$ref: '#/components/responses/Error'
post:
operationId: createNote
tags:
- Notes
summary: Create a note
description: 'Creates a text file (`.md`, `.txt`, …) of at most 5 MB. A live file at the same path
(compared without case) answers 409 `path_taken`. Needs `vaults:write`, `sync`, or
`notes:create` (with `folders:create` too when the note''s folder does not exist yet).'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NoteCreate'
responses:
'201':
description: The note.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/notes/by-path:
parameters:
- $ref: '#/components/parameters/VaultID'
get:
operationId: getNoteByPath
tags:
- Notes
summary: Read a note by its path
parameters:
- name: path
in: query
required: true
schema:
type: string
minLength: 1
maxLength: 1024
responses:
'200':
description: The note with its text.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/notes/{file_id}:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/FileID'
get:
operationId: getNote
tags:
- Notes
summary: Read a note
responses:
'200':
description: The note with its text.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
put:
operationId: updateNote
tags:
- Notes
summary: Replace a note's text
description: 'With `base_version_id`, answers 409 `version_mismatch` if the note changed since that
version; without it, the last write wins. Needs `vaults:write`, `sync`, or `notes:edit`.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NoteUpdate'
responses:
'200':
description: The note.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
patch:
operationId: patchNote
tags:
- Notes
summary: Edit part of a note
description: 'Applies `find`/`replace` (the first match, or every match with `all`), then `prepend`
and `append`. Without `base_version_id` the edit is applied to the newest text, so
concurrent edits from synced devices are kept. A missing `find` answers 400
`find_not_found`. Needs `vaults:write`, `sync`, or `notes:edit`.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NotePatch'
responses:
'200':
description: The note.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
delete:
operationId: deleteNote
tags:
- Notes
summary: Delete a note
description: 'The note stays in version history and can be restored from there. Needs
`vaults:write`, `sync`, or `notes:edit`.'
parameters:
- name: base_version_id
in: query
required: false
schema:
type: string
format: uuid
responses:
'204':
description: Deleted.
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/notes/{file_id}/move:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/FileID'
post:
operationId: moveNote
tags:
- Notes
summary: Rename or move a note
description: 'Links to the note are not rewritten. A taken path answers 409 `path_taken`. Needs
`vaults:write`, `sync`, or `notes:edit` (with `folders:create` too when the folder it
moves into does not exist yet).'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NoteMove'
responses:
'200':
description: The note, without its text.
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
$ref: '#/components/responses/Error'
components:
schemas:
NoteList:
type: object
required:
- items
properties:
items:
type: array
items:
$ref: '#/components/schemas/Note'
next_cursor:
type: string
NoteCreate:
type: object
required:
- path
- content
properties:
path:
type: string
minLength: 1
maxLength: 1024
content:
type: string
Note:
type: object
required:
- id
- path
- version_id
- size
- updated_at
properties:
id:
type: string
format: uuid
path:
type: string
version_id:
type: string
format: uuid
size:
type: integer
format: int64
updated_at:
type: string
format: date-time
content:
type: string
description: The note's text; omitted in lists and after a move.
NoteUpdate:
type: object
required:
- content
properties:
content:
type: string
base_version_id:
type: string
format: uuid
NotePatch:
type: object
properties:
find:
type: string
minLength: 1
replace:
type: string
all:
type: boolean
default: false
prepend:
type: string
append:
type: string
base_version_id:
type: string
format: uuid
Problem:
type: object
description: RFC 9457 problem details.
required:
- type
- title
- status
properties:
type:
type: string
format: uri
examples:
- https://granite.md/problems/conflict
title:
type: string
status:
type: integer
format: int32
detail:
type: string
instance:
type: string
code:
type: string
description: Machine-readable refinement of `type`, for example `version_conflict`.
request_id:
type: string
errors:
type: array
description: Per-field validation errors.
items:
$ref: '#/components/schemas/FieldError'
NoteMove:
type: object
required:
- path
properties:
path:
type: string
minLength: 1
maxLength: 1024
base_version_id:
type: string
format: uuid
FieldError:
type: object
required:
- field
- detail
properties:
field:
type: string
description: JSON path of the invalid field, for example `ops[2].path`.
detail:
type: string
headers:
RequestID:
description: Identifier of this request, for support and log correlation.
schema:
type: string
parameters:
VaultID:
name: vault_id
in: path
required: true
schema:
type: string
format: uuid
FileID:
name: file_id
in: path
required: true
schema:
type: string
format: uuid
Limit:
name: limit
in: query
required: false
description: Maximum items to return.
schema:
type: integer
format: int32
minimum: 1
maximum: 200
default: 50
Cursor:
name: cursor
in: query
required: false
description: Opaque cursor from a previous page's `next_cursor`.
schema:
type: string
maxLength: 512
responses:
Error:
description: Error response.
headers:
X-Request-ID:
$ref: '#/components/headers/RequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Session access token, personal API token, or OAuth access token.