Harbor
Developer docs menu

Start here

Conventions

The shared contract behind every endpoint — so you only have to learn it once.

Every endpoint in the API reference follows these rules. They’re documented here once instead of on every page.

JSON and field names

  • Requests and responses are JSON (Content-Type: application/json).
  • JSON field names are snake_casenotebook_id, created_at, source_url.
  • The token endpoint additionally accepts application/x-www-form-urlencoded, per the OAuth 2.0 spec.

Timestamps

All timestamps are UTC epoch milliseconds (integers):

{ "created_at": 1752600000000, "updated_at": 1752600000000 }

To convert:

# bash / GNU date
date -u -d @1752600000                     # seconds → readable
from datetime import datetime, timezone
datetime.fromtimestamp(1752600000000 / 1000, tz=timezone.utc)
new Date(1752600000000).toISOString();

The one exception is OAuth’s expires_in, which is a duration in seconds (again, per the OAuth spec).

Response envelopes

Harbor uses a small, predictable set of response shapes.

A single resource

Wrapped under a data key:

{
  "data": {
    "id": "9c2e7f1a-0b3d-4e6f-8a12-5c7d9e0f1a2b",
    "title": "Welcome to Harbor",
    "created_at": 1752600000000
  }
}

A collection

A data array plus a paging block:

{
  "data": [
    { "id": "…", "title": "First note" },
    { "id": "…", "title": "Second note" }
  ],
  "paging": { "limit": 100, "offset": 0, "total": 2, "has_more": false }
}

An OAuth token

The token endpoint returns a bare object (not wrapped in data), as the OAuth spec requires:

{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "notes notebooks"
}

Pagination

List endpoints take limit and offset query parameters:

ParameterDefaultNotes
limit100Max page size is 500.
offset0Number of items to skip.

The paging block tells you where you are:

{ "limit": 100, "offset": 0, "total": 342, "has_more": true }

Page through a collection until has_more is false:

offset=0
while : ; do
  page=$(curl -s "https://app.harbor.my/api/v1/notes?limit=100&offset=$offset" \
    -H "Authorization: Bearer $HARBOR_TOKEN")
  echo "$page" | jq -r '.data[].id'
  [ "$(echo "$page" | jq -r '.paging.has_more')" = "true" ] || break
  offset=$((offset + 100))
done

Sorting

List endpoints accept an order parameter — a comma-separated list of fields, each optionally prefixed with - for descending:

?order=-updated_at,title

That sorts by most-recently-updated first, then by title. The fields you can sort by are noted on each endpoint.

Filtering

Most list endpoints take resource-specific filters as query parameters — for example notebook_id on notes, or parent_id on tags. These are documented per endpoint.

Request IDs

Every response carries an X-Request-Id header, and errors echo it in the body as request_id. Include it when you report a problem — it lets us find the exact request in our logs.

Next steps