# lynkr API ガイド

lynkr（https://lynkr.jp）は、SNS・ショップ・動画などのリンクを1ページにまとめる「link in bio」サービスです。
すべての操作を REST API と MCP サーバーから行えるので、Claude Code や Codex などの AI エージェントからページを作成・編集できます。

- REST API: `https://lynkr.jp/api/v1`
- MCP（Streamable HTTP）: `https://lynkr.jp/mcp`
- OpenAPI: `https://lynkr.jp/openapi.json`
- CLI: `curl -fsSL https://lynkr.jp/cli.mjs -o lynkr.mjs && node lynkr.mjs help`

## 1. 認証

管理画面の「設定 → APIトークン」でトークン（`lk_` で始まる文字列）を発行し、すべてのリクエストに付けます。

```
Authorization: Bearer lk_xxxxxxxxxxxxxxxx
```

| スコープ | できること |
| --- | --- |
| read | 取得のみ |
| write | ページ・ブロック・画像の作成／変更／削除（read を含む） |
| admin | 管理者向け API（管理者ユーザーのみ発行可） |

## 2. AI エージェントから使う

### Claude Code

```bash
claude mcp add --transport http lynkr https://lynkr.jp/mcp --header "Authorization: Bearer lk_xxxxxxxx"
```

### Codex（~/.codex/config.toml）

```toml
[mcp_servers.lynkr]
url = "https://lynkr.jp/mcp"
bearer_token_env_var = "LYNKR_TOKEN"
```

環境変数 `LYNKR_TOKEN` にトークンを入れて Codex を起動します。
MCP を使わない場合は、このガイドを読ませて curl か CLI で操作させてください。

### MCP ツール

`whoami` `list_pages` `get_page` `create_page` `update_page` `replace_page` `delete_page` `add_block` `update_block` `delete_block` `reorder_blocks` `get_analytics` `upload_image_from_url` `list_images` `fetch_link_info` `get_reference`

## 3. データモデル

- **ページ**: `lynkr.jp/{slug}` で公開される1枚のページ。`title`（表示名）・`bio`（自己紹介）・`avatar_url`・`published`・`theme`・`seo`・`blocks` を持つ。1アカウント5ページまで。
- **ブロック**: ページに上から順に並ぶ部品。`{ id, type, visible, data }`。1ページ100個まで。
- ページの指定（`:ref`）には slug（`taro`）・ページID（`pg_...`）・URL のどれでも使えます。

### ブロック種別

- `link`（リンク）: ボタン型のリンク。URLからSNSアイコンを自動表示します
  - 例: `{"title":"Instagram","url":"https://www.instagram.com/lynkr_jp","description":"毎日更新中","style":"button"}`
- `social`（SNSアイコン）: SNSのアイコンを横に並べて表示。@ユーザー名だけでもOK
  - 例: `{"items":[{"service":"x","url":"@lynkr_jp"},{"service":"instagram","url":"https://www.instagram.com/lynkr_jp"},{"service":"email","url":"hello@example.com"}],"style":"mono"}`
- `heading`（見出し）: セクションの見出しテキスト
  - 例: `{"text":"お仕事のご依頼","align":"center","size":"md"}`
- `text`（テキスト）: 文章。改行・URL自動リンク・**太字**・[ラベル](URL) が使えます
  - 例: `{"text":"東京を拠点に活動するイラストレーターです。\nお仕事のご依頼は **メール** からどうぞ。","align":"center"}`
- `image`（画像）: 1枚の画像。リンクを付けることもできます
  - 例: `{"src":"https://images.example.com/cover.jpg","alt":"新作のポスター","link":"https://example.com/shop","aspect":"16:9"}`
- `gallery`（ギャラリー）: 複数の画像をグリッドまたは横スクロールで表示
  - 例: `{"images":[{"src":"https://images.example.com/1.jpg"},{"src":"https://images.example.com/2.jpg","link":"https://example.com"}],"layout":"grid2","aspect":"1:1"}`
- `embed`（動画・音楽）: YouTube / Vimeo / Spotify / SoundCloud / Apple Music / TikTok のURLを貼るだけでプレイヤーを埋め込み
  - 例: `{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","title":"最新MV"}`
- `map`（地図）: 住所や店名から Google マップを埋め込み
  - 例: `{"query":"東京タワー","title":"アクセス","zoom":15}`
- `divider`（区切り・余白）: 線・ドット・余白でブロックを区切る
  - 例: `{"style":"line","size":"md"}`

### data の早見表（* は必須）

- link（リンク）: {title*: text, url*: url, description: text, thumbnail: image, style: button|card|image, icon: auto|none|x|instagram|…(52種), highlight: none|shake|pulse|shine}
- social（SNSアイコン）: {items: [{service: x|instagram|tiktok|youtube|…(50種), url*: text}]（最大30）, style: mono|brand, size: sm|md|lg}
- heading（見出し）: {text*: text, align: left|center|right, size: sm|md|lg}
- text（テキスト）: {text*: text, align: left|center|right, style: plain|card}
- image（画像）: {src*: image, alt: text, caption: text, link: url, aspect: auto|1:1|4:3|16:9|3:4}
- gallery（ギャラリー）: {images: [{src*: image, alt: text, link: url}]（最大12）, layout: grid2|grid3|carousel, aspect: auto|1:1|4:3|16:9|3:4}
- embed（動画・音楽）: {url*: url, title: text}
- map（地図）: {query*: text, title: text, zoom: number}
- divider（区切り・余白）: {style: line|dots|space, size: sm|md|lg}

- URL はスキーム省略可（`example.com` → `https://example.com`、`a@b.jp` → `mailto:`）。
- SNS（social）の `url` は `@ユーザー名` だけでも可。`service` に使える値: x, instagram, tiktok, youtube, line, facebook, threads, bluesky, note, github, discord, twitch, spotify, applemusic, soundcloud, pinterest, mastodon, misskey, pixiv, ameba, telegram, whatsapp, patreon, substack, medium, zenn, qiita, niconico, vimeo, snapchat, reddit, kofi, buymeacoffee, etsy, tumblr, applepodcasts, kick, bilibili, steam, gumroad, behance, dribbble, figma, notion, linkedin, amazon, booth, email, phone, website
- link の `icon` は省略（`auto`）するとURLからSNSアイコンを自動表示。`none` で非表示。
- text と bio では改行・URL自動リンク・`**太字**`・`[ラベル](https://...)` が使えます。

### テーマ

```json
{
  "preset": "sunset",
  "background": { "type": "gradient", "from": "#ffa07a", "to": "#ff5e98", "angle": 160 },
  "text_color": "#ffffff",
  "font": "maru",
  "button": { "style": "shadow", "shape": "pill", "color": "#ffffff", "text_color": "#e0457b" },
  "avatar_shape": "circle"
}
```

- `preset`: simple / cream / night / midnight / sunset / ocean / mint / sakura / lavender / elegant / forest / pop / cyber（プリセットだけ指定すれば残りはプリセットの値）
- `background.type`: solid（color）/ gradient（from, to, angle）/ image（image, overlay 0〜0.9, blur 0〜30）
- `font`: system / noto_sans / maru / zen_maru / mincho / kosugi / klee / dela / dot / mono
- `button.style`: fill / outline / soft / shadow / hard / glass、`button.shape`: square / rounded / pill
- 色はすべて `#RRGGBB`

## 4. エンドポイント

| メソッド | パス | 説明 |
| --- | --- | --- |
| GET | /api/v1/me | 自分の情報とトークンのスコープ |
| GET | /api/v1/pages | ページ一覧 |
| POST | /api/v1/pages | ページ作成（blocks も同時に指定可） |
| GET | /api/v1/pages/:ref | ページ全体（ブロック含む） |
| PATCH | /api/v1/pages/:ref | 部分更新（theme / seo は JSON Merge Patch） |
| PUT | /api/v1/pages/:ref | ブロック全体を宣言的に同期（送ったフィールドは置き換え） |
| DELETE | /api/v1/pages/:ref | ページ削除 |
| POST | /api/v1/pages/:ref/blocks | ブロック追加（position 省略で末尾） |
| PATCH | /api/v1/pages/:ref/blocks/:id | ブロック更新（data は Merge Patch、position で移動） |
| DELETE | /api/v1/pages/:ref/blocks/:id | ブロック削除 |
| POST | /api/v1/pages/:ref/blocks/:id/duplicate | ブロック複製 |
| POST | /api/v1/pages/:ref/blocks/reorder | 並べ替え `{"ids": [...]}` |
| GET | /api/v1/pages/:ref/analytics?days=30 | アクセス解析（JST） |
| POST | /api/v1/images | 画像アップロード（multipart の file / 生バイナリ / `{"url": "..."}`） |
| GET | /api/v1/images | 画像一覧 |
| DELETE | /api/v1/images/:id | 画像削除 |
| GET | /api/v1/unfurl?url= | リンク先のタイトル・OGP画像を取得 |
| GET | /api/v1/meta | ブロック種別（JSON Schema）・テーマ・フォント・SNS一覧（認証不要） |
| GET | /api/v1/slugs/:slug | URL の空き状況（認証不要） |

エラーは `{"error": {"code": "...", "message": "...", "details": [{"path": "...", "message": "..."}]}}` の形式で返ります。message を読めば修正方法が分かるようにしています。

## 5. 例（curl）

```bash
export LYNKR=https://lynkr.jp/api/v1
export AUTH="Authorization: Bearer $LYNKR_TOKEN"

# ページ一覧
curl -s -H "$AUTH" $LYNKR/pages

# ページを作成（ブロック付き）
curl -s -H "$AUTH" -H 'Content-Type: application/json' $LYNKR/pages -d '{
  "slug": "taro",
  "title": "山田 太郎",
  "bio": "写真家 / 東京",
  "theme": { "preset": "midnight" },
  "blocks": [
    { "type": "social", "data": { "items": [{ "service": "instagram", "url": "@taro" }, { "service": "x", "url": "@taro" }] } },
    { "type": "link", "data": { "title": "作品集", "url": "https://example.com" } }
  ]
}'

# 先頭にリンクを追加
curl -s -H "$AUTH" -H 'Content-Type: application/json' $LYNKR/pages/taro/blocks \
  -d '{"type": "link", "position": 0, "data": {"title": "新作MV", "url": "https://youtu.be/xxxxxxxxxxx", "highlight": "shine"}}'

# ブロックのタイトルだけ変更
curl -s -X PATCH -H "$AUTH" -H 'Content-Type: application/json' $LYNKR/pages/taro/blocks/bk_xxxxxxxxxxxxxxxx \
  -d '{"data": {"title": "最新MV"}}'

# 背景色だけ変更（ほかのテーマ設定はそのまま）
curl -s -X PATCH -H "$AUTH" -H 'Content-Type: application/json' $LYNKR/pages/taro \
  -d '{"theme": {"background": {"type": "solid", "color": "#fff1f4"}}}'

# 画像をアップロードしてアイコンに設定
curl -s -H "$AUTH" -F file=@avatar.png $LYNKR/images   # → {"image": {"path": "/i/im_....webp", ...}}
curl -s -X PATCH -H "$AUTH" -H 'Content-Type: application/json' $LYNKR/pages/taro -d '{"avatar_url": "/i/im_xxxxxxxxxxxxxxxx.webp"}'
```

## 6. ページをファイルで管理する（pull / push）

`GET /api/v1/pages/:ref` の `page` オブジェクトを JSON ファイルに保存し、編集して `PUT /api/v1/pages/:ref` で送り返すと、その内容にそろいます。
id 付きのブロックは更新（アクセス解析も引き継がれる）、id なしは新規作成、ファイルにない既存ブロックは削除されます。
`blocks` 以外のフィールド（title・bio・theme・seo など）は、送ったものだけが置き換わり、省略したものは変わりません。

```bash
node lynkr.mjs pull taro            # → pages/taro.json
# pages/taro.json を編集
node lynkr.mjs push pages/taro.json
```

## 7. 注意

- 変更はすぐ公開ページに反映されます。非公開にしたいときは `published: false`。
- 画像は PNG / JPEG / WebP / GIF / AVIF、1.5MB まで（SVG は不可）。https の外部URLを直接使うこともできます。
- レート制限: 1ユーザーあたり毎分300リクエスト程度。超えると 429。
