<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Developers on Harbor</title><link>https://harbor.my/developers/</link><description>Recent content in Developers on Harbor</description><generator>Hugo</generator><language>en-US</language><atom:link href="https://harbor.my/developers/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting started</title><link>https://harbor.my/developers/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://harbor.my/developers/getting-started/</guid><description>&lt;p&gt;The Harbor API is a standard REST API over HTTPS. Requests and responses are
JSON. You authenticate with a &lt;strong&gt;personal access token&lt;/strong&gt; (for your own scripts)
or an &lt;strong&gt;OAuth app&lt;/strong&gt; (for integrations other people install). There&amp;rsquo;s also a
first-class &lt;a href="https://harbor.my/developers/cli/"&gt;command-line tool&lt;/a&gt; and an agent skill for your AI.&lt;/p&gt;
&lt;h2 id="base-url"&gt;Base URL&lt;/h2&gt;
&lt;p&gt;All API requests go to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://app.harbor.my/api/v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The API is versioned in the path (&lt;code&gt;/api/v1&lt;/code&gt;). We&amp;rsquo;ll add new versions rather than
break this one.&lt;/p&gt;</description></item><item><title>The Harbor CLI</title><link>https://harbor.my/developers/cli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://harbor.my/developers/cli/</guid><description>&lt;p&gt;&lt;code&gt;harbor&lt;/code&gt; is a small, dependency-light Go binary that exposes the &lt;strong&gt;entire
Harbor API&lt;/strong&gt; as composable commands: notebooks, notes, hierarchical tags,
full-text and OCR search, file attachments, sync, version history, trash,
templates, reminders, public sharing, client-side encryption, and Evernote
import/export. Every command takes &lt;code&gt;--json&lt;/code&gt;, so it drops straight into a
pipeline.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s open source: &lt;strong&gt;&lt;a href="https://github.com/HarborMyNotes/harbor-cli"&gt;github.com/HarborMyNotes/harbor-cli&lt;/a&gt;&lt;/strong&gt; (MIT).&lt;/p&gt;
&lt;h2 id="install"&gt;Install&lt;/h2&gt;
&lt;h4&gt;Homebrew (macOS &amp;amp; Linux)&lt;/h4&gt;
&lt;p&gt;The repo is its own tap:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew tap HarborMyNotes/harbor https://github.com/HarborMyNotes/harbor-cli
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install harbor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Update later with &lt;code&gt;brew upgrade harbor&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Authentication</title><link>https://harbor.my/developers/authentication/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://harbor.my/developers/authentication/</guid><description>&lt;p&gt;Every API request is authenticated with a &lt;strong&gt;bearer token&lt;/strong&gt; in the
&lt;code&gt;Authorization&lt;/code&gt; header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl https://app.harbor.my/api/v1/notes &lt;span style="color:#ebcb8b"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#a3be8c"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;$HARBOR_TOKEN&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two kinds of token, for two different jobs.&lt;/p&gt;
&lt;h2 id="personal-access-tokens"&gt;Personal access tokens&lt;/h2&gt;
&lt;p&gt;A personal access token (PAT) is a long-lived key that acts as &lt;strong&gt;you&lt;/strong&gt;. Use one
for your own scripts, a cron job, a CLI, or a one-off integration. Tokens start
with &lt;code&gt;hbp_&lt;/code&gt; and are scoped to exactly the permissions you pick.&lt;/p&gt;</description></item><item><title>Conventions</title><link>https://harbor.my/developers/conventions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://harbor.my/developers/conventions/</guid><description>&lt;p&gt;Every endpoint in the &lt;a href="https://harbor.my/developers/api/notes/"&gt;API reference&lt;/a&gt; follows these
rules. They&amp;rsquo;re documented here once instead of on every page.&lt;/p&gt;
&lt;h2 id="json-and-field-names"&gt;JSON and field names&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Requests and responses are &lt;strong&gt;JSON&lt;/strong&gt; (&lt;code&gt;Content-Type: application/json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;JSON field names are &lt;strong&gt;&lt;code&gt;snake_case&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;notebook_id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;,
&lt;code&gt;source_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://harbor.my/developers/authentication/#oauth-apps"&gt;token endpoint&lt;/a&gt; additionally
accepts &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, per the OAuth 2.0 spec.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="timestamps"&gt;Timestamps&lt;/h2&gt;
&lt;p&gt;All timestamps are &lt;strong&gt;UTC epoch milliseconds&lt;/strong&gt; (integers):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1752600000000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;updated_at&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1752600000000&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To convert:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# bash / GNU date&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date -u -d @1752600000 &lt;span style="color:#616e87;font-style:italic"&gt;# seconds → readable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;from&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;datetime&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;import&lt;/span&gt; datetime&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; timezone
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;datetime&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;fromtimestamp&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1752600000000&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; tz&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;timezone&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;utc&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Date&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1752600000000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;toISOString&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The one exception is OAuth&amp;rsquo;s &lt;code&gt;expires_in&lt;/code&gt;, which is a duration in &lt;strong&gt;seconds&lt;/strong&gt;
(again, per the OAuth spec).&lt;/p&gt;</description></item><item><title>Errors</title><link>https://harbor.my/developers/errors/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://harbor.my/developers/errors/</guid><description>&lt;p&gt;Harbor uses conventional HTTP status codes and returns every error in a single
JSON envelope, so you only have to handle one shape.&lt;/p&gt;
&lt;h2 id="the-error-envelope"&gt;The error envelope&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;validation_failed&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;The request was invalid.&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;details&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;is required&amp;#34;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;&amp;#34;request_id&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;req_01HX…&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;A &lt;strong&gt;stable, machine-readable&lt;/strong&gt; identifier. Branch on this, not on the message.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;message&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Human-readable and safe to show a user. Internal errors never leak specifics.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;details&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Optional. Present mainly on validation errors as a field → message map.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;request_id&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Included when available, and always in the &lt;code&gt;X-Request-Id&lt;/code&gt; response header.&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="handling-errors"&gt;Handling errors&lt;/h2&gt;
&lt;p&gt;Check the HTTP status first, then the &lt;code&gt;code&lt;/code&gt; for anything you want to handle
specifically:&lt;/p&gt;</description></item></channel></rss>