# Shager REST API クイックスタート：新しいキーから40アカウントへの計測付き投稿まで、7回の呼び出しで

_2026-09-19 · curlとTypeScriptでShagerのREST APIを実践解説。認証、ワークスペース、チャンネル、メディア、下書き、予約、状態確認、クリック。_

Shager の REST API は `https://app.shager.ai/api/v1` にあり、`Authorization: Bearer shgr_live_...` で認証します。結果はすべて `{ "data": ... }`、エラーはすべて `{ "error": { "code", "message" } }` の形で返ります。配信を一通り行うのに必要な呼び出しは7回です。キーを確認し、ワークスペースを選び、そのチャンネルを一覧し、メディアをアップロードし、アカウントごとのキャプション付きで下書きを作り、予約し、最後に状態とクリックを確認します。機械可読な仕様は [OpenAPI 3.1 ドキュメント](https://app.shager.ai/api/v1/openapi.json)にあります。

以下のコードは、環境変数を2つ設定すれば書いてあるとおりに動きます。同じ操作は同じ名前の MCP ツールとしても用意されているので、ここで覚えたことは Claude Code、Codex、Cursor でもそのまま使えます。

## 最初の呼び出しの前に必要なもの

必要なのはキーです。人が https://app.shager.ai/dashboard/agents で必要なスコープを付けて作成するか、エージェントが[自己登録](/ja/blog/agent-self-signup-and-claim)でサンドボックスを作成します。スコープは次のとおりです。

| スコープ | できること |
|---|---|
| `read` | `whoami`、ワークスペース・チャンネル・グループ・投稿の一覧、投稿の参照、分析 |
| `write` | ワークスペース、グループ、メディア、下書き投稿、接続リンク、承認リクエストの作成、取り消し |
| `publish` | 予約（`schedule_at` 付きの `create_post` を含む） |

```bash
export SHAGER_API_KEY="shgr_live_your_key_here"
export API="https://app.shager.ai/api/v1"
```

## 呼び出し1：自分は誰か

```bash
curl -s "$API/me" -H "Authorization: Bearer $SHAGER_API_KEY"
```

```json
{ "data": { "organization_id": "...", "organization": "Northwind Agency", "sandbox": false, "scopes": ["read", "write"] } }
```

`sandbox` が true の場合、人がキーを引き継ぐまで予約と接続リンクは使えません。`scopes` に `publish` がなければ、下書きは作れても予約はできません。最初にこれを把握しておけば、後でわけのわからない 403 に悩まされずに済みます。

## 呼び出し2：ワークスペースを選ぶか作成する

ワークスペースは通常、1つのクライアントまたはブランドにあたります。

```bash
curl -s "$API/workspaces" -H "Authorization: Bearer $SHAGER_API_KEY"

curl -s -X POST "$API/workspaces" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Summit Physio", "timezone": "Europe/London", "approval_required": true}'
```

作成時には必ず `timezone`（IANA の名前）を設定してください。予約時刻はオフセット付きで保存され、Shager はワークスペースのタイムゾーンを使って人に時刻を表示します。`approval_required: true` にすると、投稿はクライアントの承認を得てからでないと予約できません。新しいクライアントには、これをデフォルトにしておくのがおすすめです。

以降のコマンドが動くように、レスポンスの id を環境変数に設定します。

```bash
export WS="the_workspace_id"
```

## 呼び出し3：チャンネルとグループを一覧する

```bash
curl -s "$API/workspaces/$WS/channels" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/groups" -H "Authorization: Bearer $SHAGER_API_KEY"
```

各チャンネルは `id`、`network`、`name`、`handle`、`status`、対応している `formats`、そして `connected_by_client` とともに返ります。グループは `id`、`name`、`channel_ids` を返します。グループを使えば、40個の id を並べなくても40のアカウントを指定できます。`create_post` に `group_ids` を渡すと、そのグループ内の有効なチャンネルがすべて含まれます。

ワークスペースにまだチャンネルがない場合は、`POST /workspaces/{workspace_id}/connect-links` で接続リンクを作成し、アカウントの持ち主に送ります。持ち主は Shager にログインすることなく、Facebook、Instagram、YouTube、X、LinkedIn、TikTok を自分で接続できます。

## 呼び出し4：URL からメディアをアップロードする

```bash
curl -s -X POST "$API/workspaces/$WS/media" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://cdn.summitphysio.example/video/knee-mobility.mp4",
    "file_name": "knee-mobility.mp4",
    "width": 1080, "height": 1920, "duration_ms": 42000
  }'
```

Shager はサーバー側でファイルをダウンロードし、`id`、`kind`、`mime_type`、`size_bytes` を返します。リール、ショート、ストーリーズの場合、わかっていれば `width`、`height`、`duration_ms` を渡してください。そうすれば Shager が、送信時に失敗するのではなく、投稿の予約前に各 SNS の形式ルールをチェックできます。

## 呼び出し5：アカウントごとのキャプション付きで下書きを作る

ここが肝心の呼び出しです。素の `fetch` を使った TypeScript のコードで、繰り返し使える小さなヘルパーも含めています。

```typescript
const API = "https://app.shager.ai/api/v1";

async function shager<T>(method: string, path: string, body?: unknown): Promise<T> {
  const res = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.SHAGER_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) {
    const err = new Error(`${res.status} ${json.error.code}: ${json.error.message}`);
    (err as any).status = res.status;
    throw err;
  }
  return json.data as T;
}

const WS = process.env.SHAGER_WORKSPACE_ID!;
const mediaId = process.env.SHAGER_MEDIA_ID!; // the id returned by call 4
const channels = await shager<Array<{ id: string; name: string; network: string; status: string }>>(
  "GET", `/workspaces/${WS}/channels`,
);

const post = await shager<{ id: string; status: string }>("POST", `/workspaces/${WS}/posts`, {
  label: "Knee mobility, week 42",
  caption: "Three minutes a day for stiffer knees. Full routine in the video.",
  channel_ids: channels.map((c) => c.id),
  media_ids: [mediaId],
  format: "REEL",
  youtube_title: "3-minute knee mobility routine",
  youtube_privacy: "public",
  per_channel: channels.map((c) => ({
    channel_id: c.id,
    caption: `${c.name}: three minutes a day for stiffer knees. Our physios show the routine.`,
  })),
  cta_url: "https://summitphysio.example/book",
  cta_text: "Book an assessment",
  campaign: "knee-mobility-oct",
  first_comment: "Book an assessment: https://summitphysio.example/book",
  stagger_seconds: [45, 120],
});

console.log(post.id, post.status); // DRAFT
```

各フィールドで得られるものは次のとおりです。

1. `per_channel` は、アカウントごとに専用のキャプションを与えます。実際にはエージェントがこれを書きます。上のテンプレートはそのステップの仮置きで、理由は[大規模投稿でスパム判定を避ける方法](/ja/blog/avoid-spam-blocks-when-posting-at-scale)で解説しています。
2. `format` は、その SNS が対応していれば適用され、そうでなければ `SHORT` と `REEL` のどちらにするかが SNS ごとに判断されます。`youtube_title` と `youtube_privacy` は YouTube のチャンネルにだけ影響します。
3. `cta_url` からは、アカウントごとに別々の短縮リンクと UTM のセットが作られ、`utm_content` でアカウントを識別できます。[アカウント単位のリード計測](/ja/blog/utm-per-account-lead-attribution)はこの仕組みの上に成り立っています。
4. `stagger_seconds` は、同じ SNS のアカウント間にランダムな待ち時間を設定します。デフォルトは `[30, 90]` です。

`schedule_at` を付けなければ、結果は下書きになります。次の呼び出しのために、`export POST_ID="the_post_id"` で id を控えておきましょう。

## 呼び出し6：承認または予約

ワークスペースが承認を必須にしている場合は、クライアントに承認を依頼します。レスポンスには `approval_url` が含まれ、承認者はログインせずにこれを開けます。

```bash
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/approval" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"reviewer_name": "Dana Ellis", "reviewer_email": "dana@summitphysio.example", "send_email": true}'
```

承認されたら（または承認が不要なら）予約します。これには `publish` スコープが必要で、`schedule_at` はオフセット付きの ISO 8601 でなければなりません。

```bash
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/schedule" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"schedule_at": "2026-10-20T08:30:00+01:00"}'
```

この時点で Shager は、すべての投稿先を各 SNS のルールに照らして検証します。どれかのアカウントが投稿を拒否しそうな場合は、理由とともに 422 `rejected` が返り、何もキューに入りません。

## 呼び出し7：状態とクリックを確認する

```bash
curl -s "$API/workspaces/$WS/posts/$POST_ID" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/posts?status=SCHEDULED&limit=50" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/analytics?from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z" \
  -H "Authorization: Bearer $SHAGER_API_KEY"
```

`get_post` は投稿先ごとに1行を返し、`state`、`dispatch_at`、`published_at`、公開された投稿の `url`、そしてエラーがあれば `error` を含みます。分析では `total_clicks`、`by_account`、`by_post` が返り、Shager の計測リンクのクリックを、ボットのトラフィックを除いて集計します。公開が始まっていない予約済みの投稿を取り消すには、`POST /workspaces/{workspace_id}/posts/{post_id}/cancel` を使います。

## コードで処理すべきエラー

| ステータスとコード | 意味 | 対処 |
|---|---|---|
| 401 `unauthorized` | ヘッダーがない、または形式が不正 | `Authorization: Bearer shgr_...` を送る |
| 403 `forbidden` | キーに必要なスコープがない | 下書きにとどめるか、人に `publish` を依頼する |
| 403 `sandbox_unclaimed` | 引き継がれていない自己登録キー | 引き継ぎ用リンクを人に伝える |
| 404 `not_found` | ワークスペース、投稿、チャンネルの id が違う | 一覧を取り直す。id はワークスペースごと |
| 409 `not_scheduled` | 予約されていない投稿を取り消そうとした | 先に投稿の状態を確認する |
| 422 `rejected` | SNS のルールに違反した | メッセージを表示する。問題点が書かれている |
| 429 `rate_limited` | キーごとの毎分120リクエストを超えた | 間隔を空けてリトライする |

うまく設計されたクライアントなら、レート制限に引っかかることはまずありません。1回の `create_post` で数百のアカウントをカバーできるので、アカウントごとに呼び出す必要はないはずです。

## よくある質問

### SDK はありますか？

専用の SDK はありません。型付きのクライアントが欲しければ、OpenAPI 3.1 ドキュメントを一般的なジェネレーターで使えます。API は小さいので、たいていは上のようなヘルパーがあれば十分です。Shager の CLI は近日提供予定です。

### REST と MCP は本当に同じですか？

はい。各 MCP ツールは1つの REST 操作に対応しており、名前、入力、スコープ、レート制限、監査ログもすべて同じです。スクリプトやサービスからは REST を、エージェントのクライアントからは MCP を使ってください。ツールの一覧は[エージェント向けページ](/ja/agents)にあります。

### 1つの投稿で、Facebook と YouTube に別々のテキストを投稿するには？

`per_channel` で各チャンネルの id とキャプションを指定し、YouTube のタイトルには `youtube_title` を使います。1つの投稿オブジェクトに多数の投稿先があり、必要に応じてそれぞれに専用のキャプション、形式、最初のコメントを持たせられます。

## 次のステップ

この一連の流れが手作業で問題なく動くようになったら、エージェントが繰り返し実行できる形にまとめましょう。[Shager 用の Claude スキルを作る](/ja/blog/build-a-claude-skill-for-shager)では、これらの呼び出しとそれにまつわるルールを、再利用できるスキルにまとめる方法を紹介しています。

---
Source: https://shager.ai/ja/blog/shager-rest-api-quickstart
