Documentation
How skill spheres are weighted
Every skill in a cloud has a depth between 0 and 1, shown as a score out of 10. The sphere's size follows that depth, so a cloud can be read at a glance: big spheres are what the person is deep in, small ones are working knowledge or interests.
Where depth comes from. When a cloud is built from a résumé, depth reflects evidence in the text, not self-assessment: repeated, senior, long-running work on a topic scores high; a single mention scores low. Roughly: 2/10 is a mentioned interest, 5/10 working knowledge, 8/10 deep sustained experience, 9.5/10 expert-level evidence across many contexts.
The depth budget. The total depth across a cloud is capped by experience: a cloud earns a budget of min(18, 3 + 1.1 × years) total points, where years is the person's estimated professional experience. If a cloud's skills add up to more than the budget, every depth is scaled down proportionally. That is why adding skills dilutes the rest: nobody is 10/10 in a hundred topics, and the budget makes breadth visibly trade off against depth.
Manually added skills. When you add a skill yourself (in your cloud, or claiming one from a project), you state your years of hands-on experience with it and the depth follows 0.15 + 0.09 × years, capped between 1.5/10 and 9.5/10, so about 4 years lands at working knowledge and about 7 years at deep experience. The skill is marked as manually added, and the overall budget still applies afterwards.
Versioning.Clouds carry the version of the scoring method that built them. When the method improves, older clouds show a rebuild notice so their scores stay comparable with everyone else's.
API
A personal API token reads and writes your own cloud, nothing else. There is no extraction endpoint: building a cloud from a résumé stays in the signed-in app, and the API takes ready-made skills.
1. Create a token
- Open Settings and scroll to API tokens.
- Give the token a label that says where it will live (“website sync”, “cv pipeline”) and press Create token.
- Copy the
ph_...value immediately. It is shown exactly once; we store only a hash. Treat it like a password. - Revoke a leaked or retired token in the same list at any time. You can hold up to 5.
Every request authenticates with the token as a bearer header: Authorization: Bearer ph_your_token
2. Read your cloud
GET /api/v1/cloud returns your private cloud: title, skills with depth and category, estimated years, scoring version, and last update time.
curl https://pairharbor.com/api/v1/cloud \
-H "Authorization: Bearer ph_your_token"
{
"title": "John D. · AI Engineering Leader",
"skills": [
{ "name": "Postgres Tuning", "depth": 0.72, "category": "engineering" },
{ "name": "Team Leadership", "depth": 0.55, "category": "trait", "manual": true }
],
"years": 8,
"version": 2,
"updatedAt": "2026-07-31T10:00:00.000Z"
}The same call from JavaScript and Python:
// JavaScript (browser or Node 18+)
const res = await fetch("https://pairharbor.com/api/v1/cloud", {
headers: { Authorization: `Bearer ${process.env.PAIRHARBOR_TOKEN}` },
});
const cloud = await res.json();
console.log(cloud.skills.length, "skills");# Python
import os, requests
r = requests.get(
"https://pairharbor.com/api/v1/cloud",
headers={"Authorization": f"Bearer {os.environ['PAIRHARBOR_TOKEN']}"},
)
r.raise_for_status()
for skill in r.json()["skills"]:
print(f"{skill['name']}: {round(skill['depth'] * 10)}/10")3. Import or update your cloud
PUT /api/v1/cloud replaces your cloud's skills (and optionally its title) with what you send. Useful for keeping PairHarbor in sync with a skills file you maintain elsewhere.
curl -X PUT https://pairharbor.com/api/v1/cloud \
-H "Authorization: Bearer ph_your_token" \
-H "Content-Type: application/json" \
-d '{
"title": "My imported cloud",
"skills": [
{ "name": "Rust", "depth": 0.8, "category": "engineering" },
{ "name": "Causal Inference", "depth": 0.6, "category": "domain" },
{ "name": "Team Leadership", "depth": 0.5, "category": "trait" }
]
}'
{ "ok": true, "skills": [ ...the normalized result... ] }Rules applied to every write: categories are engineering, domain, product, craft, and trait; depths are clamped to 0–1; skill names pass the same safety filters as everywhere else (no emails, credentials, or personal data); and the depth budget re-normalizes the result when your cloud's years are known, so totals stay proportional to experience. The response contains the cloud as actually stored; read it back rather than assuming your input survived unchanged.
4. Account and workspaces
The same token also answers who it belongs to and reaches the workspaces the account is a member of. All of these are scoped exactly like the app: you see only your own account and the workspaces you are in.
GET /api/v1/me # owner id, provider, display name, email, cloud title
GET /api/v1/workspaces # Public first (kind "public"), then your company workspaces
# (kind "company"; invite token on admin rows only)
GET /api/v1/projects # your PUBLIC projects: handle, url, listed, skills
GET /api/v1/workspaces/<id>/projects # a company workspace's projects (members only)
POST /api/v1/workspaces/<id>/projects # { "title": "...", "skills": [...] } — ready-made skills
DELETE /api/v1/workspaces/<id>/projects?id=<projectId> # poster or adminPublic is the first workspace in the list on purpose: it is the open site, so “which workspace am I in” always has an explicit answer. Public projects are read-only over the API in v1, since posting one creates a public page and can list it in the Harbor; post those in the app.
Errors and limits
| 401 | Missing or invalid token. Check the Authorization header. |
| 404 | GET only: you have no cloud yet. Build one in the app first. |
| 400 | PUT only: empty or invalid skills array, or malformed JSON. |
| 429 | Daily write limit reached: 100 writes per token per day. |
MCP server for Claude Code
PairHarbor ships an MCP server, so Claude Code (or any MCP client) can read and update your cloud and work inside your workspaces as tools. It authenticates with the same personal API token and talks to the v1 API above, so every safety filter, membership gate, and write limit applies unchanged.
- Create a token in Settings (API tokens) and export it as
PAIRHARBOR_TOKENin your environment. Never commit it. - In the PairHarbor repository, run
cd mcp && npm installonce. The repo's.mcp.jsonalready registers the server for Claude Code; from any other client, register it with the snippet below.
{
"mcpServers": {
"pairharbor": {
"command": "node",
"args": ["/path/to/pairharbor/mcp/server.mjs"],
"env": { "PAIRHARBOR_TOKEN": "ph_your_token" }
}
}
}Tools: whoami, get_my_cloud, update_my_cloud, add_skills, remove_skills, list_workspaces, list_workspace_projects, post_workspace_project, delete_workspace_project. There is deliberately no extraction tool: mapping a résumé or a project description stays in the signed-in app, and the tools take ready-made skills.
Embed a cloud in your website
Every published cloud has an iframe-friendly page at /u/<handle>/embed: the interactive 3D scene alone, no site chrome, with a small PairHarbor mark linking back to the full page.
- Publish your cloud (the share button on My cloud). You get a link like
pairharbor.com/u/ab12cd34. - The handle is the part after /u/. Append /embed to get the embeddable page.
- Paste the iframe below into your site's HTML.
<iframe
src="https://pairharbor.com/u/YOUR_HANDLE/embed"
width="100%"
height="480"
style="border: 0; border-radius: 12px"
loading="lazy"
title="My skill cloud"
></iframe>For a responsive embed that keeps its shape on phones, wrap it in an aspect-ratio box instead of a fixed height:
<div style="aspect-ratio: 16 / 9; max-width: 900px">
<iframe
src="https://pairharbor.com/u/YOUR_HANDLE/embed"
width="100%"
height="100%"
style="border: 0; border-radius: 12px"
loading="lazy"
title="My skill cloud"
></iframe>
</div>Append ?mode=genesis to the embed URL to play the cloud as the Genesis animation — the skills accreting into orbit like a forming solar system, looping — instead of the static sphere.
Good to know: the embed shows exactly what the public page shows, nothing more. It does not need the cloud to be listed in the Harbor, only published. The page is live, so skill edits and renames appear in the embed automatically; revoking the link in Settings kills it. Visitors' browsers render the scene locally with WebGL; browsers without it see a plain fallback. Any site that allows iframes works, including Notion, WordPress, and personal sites.
GitHub README and other no-iframe sites
GitHub profile READMEs (and platforms like dev.to) strip iframes, but they render images. Your cloud's preview card is an image, so this Markdown gives you a branded, clickable card that lands on the live 3D cloud:
[](https://pairharbor.com/u/YOUR_HANDLE)The card regenerates from your current cloud; GitHub caches images for a few hours, so edits take a little while to appear there.
Questions the docs don't answer? See the privacy policy for how data is handled, or the terms.