Docs

Hjarni REST API

The REST API is for scripts, automations, and custom integrations. If you are connecting an AI assistant like ChatGPT or Claude, use the MCP server instead.

Overview

Base URL
https://hjarni.com/api/v1
Auth
Bearer token
Content type
application/json
Resources
Dashboard, search, notes, containers, and tags

Authentication

  1. Go to Settings > Connections in Hjarni.
  2. Create a token for your integration.
  3. Send it in the Authorization header as a Bearer token.
curl https://hjarni.com/api/v1/notes \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Pagination

List endpoints accept page (default 1) and per_page (default 25, max 100). Metadata is returned in response headers:

X-Total-Count
Total number of records matching the query
X-Page
Current page number
X-Per-Page
Items per page

Dashboard

GET /api/v1/dashboard

Overview of your personal knowledge base with counts and recent notes.

Response example
{
  "inbox_count": 3,
  "notes_count": 42,
  "archived_count": 7,
  "containers_count": 8,
  "tags_count": 12,
  "recent_notes": [
    {
      "id": 1,
      "title": "Weekly review",
      "body": "# Decisions\n\n...",
      "summary": "Review of the week",
      "source_url": null,
      "archived": false,
      "favorited": false,
      "position": 1,
      "created_at": "2026-03-15T10:00:00Z",
      "updated_at": "2026-03-15T10:00:00Z",
      "tag_list": "review,planning",
      "tags": [{"id": 1, "name": "review"}, {"id": 2, "name": "planning"}],
      "container": {"id": 5, "name": "Work"},
      "files": []
    }
  ]
}

Notes

GET /api/v1/notes

List personal notes. Paginated.

scope
"archived", "inbox", "favorited", or omit for active notes.
container_id
Filter by container.
tag
Filter by tag name.
q
Full-text search within listed notes.
page, per_page
Pagination.
GET /api/v1/notes/inbox

Notes without a container. Paginated. Same response shape as listing notes.

GET /api/v1/notes/:id

Full note with body, tags, container, files, and linked note IDs.

Response example
{
  "id": 1,
  "title": "Weekly review",
  "body": "# Decisions\n\nSee [[42:Project plan]].",
  "summary": "Review of the week",
  "source_url": "https://example.com/source",
  "archived": false,
  "favorited": true,
  "position": 1,
  "created_at": "2026-03-15T10:00:00Z",
  "updated_at": "2026-03-15T12:30:00Z",
  "tag_list": "review,planning",
  "tags": [
    {"id": 1, "name": "review"},
    {"id": 2, "name": "planning"}
  ],
  "container": {"id": 5, "name": "Work"},
  "files": [
    {
      "id": 10,
      "description": "Slide deck",
      "filename": "slides.pdf",
      "content_type": "application/pdf",
      "byte_size": 204800,
      "url": "https://hjarni.com/rails/active_storage/blobs/..."
    }
  ],
  "linked_note_ids": [42, 58]
}
POST /api/v1/notes

Create a note. Returns 201 on success.

title
Required. Note title.
body
Markdown content. Supports [[id:Title]] wiki-links.
summary
Short summary for quick scanning.
source_url
Canonical source URL.
container_id
Place the note in a container. Omit for inbox.
tag_list
Comma-separated tag names, e.g. "review,planning".
position
Sort position within the container. Defaults to end.
POST /api/v1/notes
{
  "note": {
    "title": "Weekly review",
    "body": "# Weekly review\n\nKey decisions...",
    "summary": "Review of the week",
    "source_url": "https://example.com/source",
    "container_id": 12,
    "tag_list": "review,planning"
  }
}
PATCH /api/v1/notes/:id

Update any note field. Only include the fields you want to change.

PATCH /api/v1/notes/1
{
  "note": {
    "body": "Updated content",
    "tag_list": "review,done"
  }
}
DELETE /api/v1/notes/:id

Permanently delete a note. Returns 204.

Note actions

PATCH /notes/:id/archive

Archive a note. Returns the updated note.

PATCH /notes/:id/unarchive

Restore an archived note. Returns the updated note.

PATCH /notes/:id/favorite

Mark a note as favorited. Returns the updated note.

PATCH /notes/:id/unfavorite

Remove from favorites. Returns the updated note.

PATCH /notes/:id/move

Move to another container. Send {"container_id": 5}.

Note links

POST /notes/:id/link

Link two notes bidirectionally. Send {"target_note_id": 42}. Returns the source note.

DELETE /notes/:id/unlink

Remove the link. Send {"target_note_id": 42}. Returns 204.

GET /notes/:id/linked

List all notes linked to this note. Returns an array of note objects.

Containers

GET /api/v1/containers

List containers. Paginated. Returns root-level containers by default.

scope
"archived", "all" (all active, flat), or omit for active roots only.
Response example
[
  {
    "id": 5,
    "name": "Work",
    "description": "Day job projects",
    "position": 1,
    "archived": false,
    "llm_instructions": "Use formal tone.",
    "created_at": "2026-03-01T09:00:00Z",
    "updated_at": "2026-03-15T10:00:00Z",
    "notes_count": 12,
    "children_count": 3,
    "parent": null
  }
]
GET /api/v1/containers/:id

Single container with counts and parent info.

POST /api/v1/containers

Create a container. Returns 201.

name
Required. Container name.
description
Optional description.
parent_id
Nest under a parent container. Omit for root.
llm_instructions
AI instructions scoped to this container.
position
Sort position among siblings. Defaults to end.
POST /api/v1/containers
{
  "container": {
    "name": "Research",
    "description": "Papers and reading notes",
    "parent_id": 5
  }
}
PATCH /api/v1/containers/:id

Update name, description, parent, position, or instructions.

DELETE /api/v1/containers/:id

Delete a container. Notes inside it are moved to the inbox. Child containers become root-level. Returns 204.

Container actions

PATCH /containers/:id/archive

Archive. Returns the updated container.

PATCH /containers/:id/unarchive

Restore. Returns the updated container.

GET /containers/:id/children

Direct child containers. Returns an array of container objects.

GET /containers/:id/notes

Notes in this container. Paginated.

GET /containers/:id/tree

Container with its ancestor chain and direct children.

PATCH /containers/reorder

Reorder containers. Send {"ordered_ids": [3,1,2]} with all IDs in the scope. Optional parent_id to reorder children of a specific container. Returns 204.

Root instructions

GET /containers/root_instructions

Get your personal root AI instructions. Returns {"personal_llm_instructions": "..."}.

PATCH /containers/update_root_instructions

Update root instructions. Send {"personal_llm_instructions": "..."}.

Tags

GET /api/v1/tags

List all tags with note counts. Paginated.

Response example
[
  {
    "id": 1,
    "name": "review",
    "created_at": "2026-03-01T09:00:00Z",
    "updated_at": "2026-03-15T10:00:00Z",
    "notes_count": 8
  }
]
GET /api/v1/tags/:id

Single tag with note count.

POST /api/v1/tags

Create a tag. Returns 201.

POST /api/v1/tags
{
  "tag": {"name": "research"}
}
PATCH /api/v1/tags/:id

Rename a tag. Send {"tag": {"name": "new-name"}}.

DELETE /api/v1/tags/:id

Delete a tag. Returns 204. Notes keep their content; only the tag association is removed.

Tag actions

PATCH /tags/:id/merge

Merge this tag into another. Send {"target_id": 5}. All notes are moved to the target tag and this tag is deleted. Returns the target tag.

GET /tags/:id/notes

List notes with this tag. Paginated. Returns note objects.

Errors

401

Missing or invalid token.

403

Plan-gated action, such as exceeding the Free plan note limit or attempting file uploads without a paid plan. Response includes an error message and upgrade_url.

404

Resource not found or not accessible by your account.

422

Validation failed. Response includes an errors array with messages.

Related docs

Questions about the API?

Email [email protected] and we'll help.