API リファレンス

ベース URL は https://api.sealgate.jp/v1。すべて JSON で、キーは snake_case です。

概要

パスキーの登録とログインは、どちらも「options を取る → ブラウザで認証器を動かす → response を verify に送る」の 2 段です。ブラウザ側はブラウザ SDK が行い、開発者のサーバーは SDK から届いた JSON に API キーを付けて SealGate に中継します。API キーはブラウザに出しません。

  1. ブラウザ: sg.login() → 開発者のサーバー (optionsUrl)
  2. 開発者のサーバー: POST /v1/login/options → challenge_id と options を SDK に返す
  3. ブラウザ: 認証器で署名 → 開発者のサーバー (verifyUrl)
  4. 開発者のサーバー: POST /v1/login/verify → external_id を受け取り、自分のセッションを発行する

認証

管理画面で発行した API キーを Authorization ヘッダーに付けます。キーはプロジェクトの環境 (本番・テスト。rp_id の単位) ごとです。API ではこの環境をアプリ (app) と呼びます。本番環境のキーは sk_live_、テスト環境のキーは sk_test_ で始まります。テスト環境の利用者は料金 (MAU) の対象外です。

Authorization: Bearer sk_live_...
Content-Type: application/json

登録

POST/v1/register/options

名前型説明
external_id必須string開発者側の利用者 ID (200 文字まで)。SealGate はこれ以外の個人情報を持たない
display_name必須string認証ダイアログに出す名前 (メールアドレスやニックネーム)
signed_inboolean既に鍵を持つ利用者に鍵を足すときに true。開発者のサーバーがログイン済みの本人であることを確認したうえで付ける
recovery_tokenstring端末をなくした利用者が再登録するときの復旧トークン (/v1/recovery/start で発行)

既に鍵を持つ利用者には、signed_in: true か recovery_token のどちらかが必要です。どちらも無い場合は 403 (recovery_not_allowed) を返します。signed_in はブラウザから届いた値をそのまま使わず、開発者のサーバーのセッションで決めてください。既存の鍵は options.excludeCredentials に入るため、同じ認証器での二重登録は認証器が断ります。 authenticatorSelection.residentKey はアプリの設定 (resident_key) の値です。

応答 (options は WebAuthn の PublicKeyCredentialCreationOptions):

{
  "challenge_id": "chl_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "options": {
    "rp": { "id": "example.jp", "name": "Example" },
    "user": { "id": "…", "name": "taro@example.jp", "displayName": "taro@example.jp" },
    "challenge": "…",
    "pubKeyCredParams": [{ "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 }],
    "authenticatorSelection": { "residentKey": "preferred", "userVerification": "preferred" },
    "timeout": 300000
  }
}

POST/v1/register/verify

名前型説明
challenge_id必須stringoptions で受け取った値。1 回限り、5 分で失効
response必須objectSDK (認証器) が返した RegistrationResponseJSON をそのまま
namestring端末の呼び名 (64 文字まで。超えた分は切り詰める)

応答 (201):

{
  "credential_id": "crd_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2"
}

ログイン

POST/v1/login/options

名前型説明
external_idstring指定するとその利用者の鍵だけを許可する。省略すると端末に保存された鍵から選ぶ (利用者名を入れないログイン)

応答 (options は PublicKeyCredentialRequestOptions):

{
  "challenge_id": "chl_01j8x2k9m4n5p6q7r8s9t0v1w3",
  "options": {
    "rpId": "example.jp",
    "challenge": "…",
    "allowCredentials": [{ "type": "public-key", "id": "…", "transports": ["internal"] }],
    "userVerification": "preferred",
    "timeout": 300000
  }
}

POST/v1/login/verify

名前型説明
challenge_id必須stringoptions で受け取った値
response必須objectSDK が返した AuthenticationResponseJSON をそのまま

応答。以後のセッション発行は開発者側で行います:

{
  "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "external_id": "user-123",
  "credential_id": "crd_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "user_verified": true,
  "password_allowed": true
}

password_allowed はアプリの併存期限を過ぎると false になります。

端末 (パスキー)

GET/v1/users/{external_id}/credentials

応答:

{
  "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "data": [
    {
      "id": "crd_01j8x2k9m4n5p6q7r8s9t0v1w2",
      "name": "iPhone",
      "transports": ["internal", "hybrid"],
      "aaguid": "fbfc3007-154e-4ecc-8c0b-6e020557d7bd",
      "backup_eligible": true,
      "backed_up": true,
      "created_at": "2026-09-01T02:15:00Z",
      "last_used_at": "2026-09-22T09:40:12Z",
      "revoked_at": null
    }
  ]
}

PATCH/v1/credentials/{credential_id}

PATCH/v1/users/{external_id}/credentials/{credential_id}

名前型説明
name必須string端末の呼び名 (1〜64 文字)。前後の空白と制御文字は取り除く

端末の呼び名を変えます。応答は一覧の data と同じ形の 1 件です。失効済みの鍵と、external_id を指定した場合にその利用者の鍵でないものは 404 (not_found) を返します。

DELETE/v1/credentials/{credential_id}

鍵を 1 本失効します。

POST/v1/users/{external_id}/revoke

利用者の鍵をすべて失効します (乗っ取り時など)。利用者と監査ログは残ります。

{ "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2", "revoked": 2 }

DELETE/v1/users/{external_id}

利用者を削除します (退会や個人情報の削除依頼)。鍵、登録とログインの challenge、復旧トークン、表示名を削除します。監査ログは件数と時刻を残し、end_user_id を deleted に、鍵の ID・IP・User-Agent・detail を空にします。取り消せません。同じ external_id で登録し直すと別の利用者になります。

{
  "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2",
  "deleted": true,
  "credentials_deleted": 2
}

復旧

端末をなくした利用者の再登録です。本人確認は開発者側で済ませ、その後にこの API を呼びます。SealGate は 15 分有効の復旧トークンを返すだけで、本人確認は行いません。

POST/v1/recovery/start

名前型説明
external_id必須string利用者 ID
reasonstring監査ログに残す理由 (例: 窓口で本人確認)

応答。recovery_token を /v1/register/options に渡します:

{
  "recovery_token": "rcv_01j8x2k9m4n5p6q7r8s9t0v1w4",
  "expires_at": "2026-09-22T10:00:00Z"
}

アプリの設定

GET/v1/apps/{app_id}

応答。app_id は API キーのアプリと一致している必要があります:

{
  "id": "app_01j8x2k9m4n5p6q7r8s9t0v1w6",
  "name": "Example",
  "rp_id": "example.jp",
  "rp_name": "Example",
  "origins": ["https://example.jp"],
  "resident_key": "preferred",
  "password_coexist_until": null,
  "webhook_url": null
}

PATCH/v1/apps/{app_id}

名前型説明
resident_key必須string登録時に認証器へ求める residentKey。required / preferred / discouraged (既定 preferred)

API キーで変更できるのは resident_key だけです。rp_id・許可オリジン・Webhook URL は管理画面で変更します。利用者名を入れないログインや入力欄の自動入力だけで運用する場合は required にします。管理画面のアプリ設定でも変更できます。

監査ログと数字

GET/v1/apps/{app_id}/audit

名前型説明
eventstring種別で絞る (login.succeeded など)
sincestringこの日時以降 (ISO 8601)
limitnumber件数 (既定 200、最大 1000)

応答 (新しい順):

{
  "data": [
    {
      "id": "log_01j8x2k9m4n5p6q7r8s9t0v1w5",
      "event": "login.succeeded",
      "ok": true,
      "end_user_id": "eu_01j8x2k9m4n5p6q7r8s9t0v1w2",
      "credential_id": "crd_01j8x2k9m4n5p6q7r8s9t0v1w2",
      "ip": "203.0.113.10",
      "user_agent": "Mozilla/5.0 (iPhone; …)",
      "detail": null,
      "at": "2026-09-22T09:40:12Z"
    }
  ]
}

GET/v1/apps/{app_id}/stats

応答。app_id は API キーのアプリと一致している必要があります:

{
  "mau": [
    { "month": "2026-04", "users": 812 },
    { "month": "2026-05", "users": 903 },
    { "month": "2026-06", "users": 1240 },
    { "month": "2026-07", "users": 1388 },
    { "month": "2026-08", "users": 1502 },
    { "month": "2026-09", "users": 1097 }
  ],
  "registrations": 214,
  "logins": { "succeeded": 4210, "failed": 38 },
  "users": { "total": 1830, "with_passkey": 1502 },
  "transports": { "internal": 1420, "hybrid": 61, "usb": 21 }
}

Webhook

管理画面の「環境の設定」で Webhook URL (https) を設定すると、次の出来事を JSON で POST します: register.completed、register.failed、login.succeeded、login.failed、credential.revoked、user.revoked、user.deleted、recovery.issued。再送はしません。

POST <webhook_url>
X-SealGate-Event: login.succeeded
X-SealGate-Signature: sha256=<HMAC-SHA256(本文, 署名鍵) の hex>

{ "id": "evt_...", "event": "login.succeeded", "app_id": "app_...", "at": "2026-09-22T00:00:00.000Z",
  "data": { "end_user_id": "eu_...", "external_id": "user-123", "credential_id": "crd_..." } }

署名鍵は URL を設定したときに管理画面に出ます。受け取ったら本文をそのまま HMAC-SHA256 して比べてください。

import crypto from 'node:crypto';
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex');
const ok = expected.length === sig.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));

レート制限

1 分あたりのリクエスト数の上限は、API キーごとに 600 (Free)、3,000 (従量課金・Enterprise) です。組織のすべての API キーの合計は 1,200 (Free)、6,000 (従量課金・Enterprise) です。超えると 429 (rate_limited) と Retry-After ヘッダーを返します。MCP のバッチは 20 件までで、1 件ずつ数えます。管理画面のログインは、15 分あたり IP ごとに 30 回、メールアドレスごとに 10 回までです。

MCP (AI エージェント)

MCP 対応のエージェント (Claude、Claude Code、Cursor、Codex など) から SealGate を扱えます。クライアントに MCP の URL を追加すると、ブラウザで管理画面のログイン (メール・Google・GitHub・パスキー) と許可の画面が開き、許可したアカウントとして操作します (OAuth 2.1)。はじめての方は、そのログインの画面から登録できます。できることは管理画面と同じ役割 (viewer / developer / admin / owner) で決まります。

MCP:      https://api.sealgate.jp/v1/mcp   (Streamable HTTP、POST)
スキル:   https://sealgate.jp/skill/SKILL.md
プラグイン (Claude Code):
  /plugin marketplace add https://sealgate.jp/plugin/marketplace.json
  /plugin install sealgate@sealgate
接続だけなら:
  claude mcp add --transport http sealgate https://api.sealgate.jp/v1/mcp

ログイン (OAuth) で使う tool

list_organizations (無ければ create_organization) → list_projects / list_environments で環境の ID (app_id) を得て、環境の tool に渡します。組織の tool には organization_id を渡します。

名前型説明
組織・プロジェクトorganization_idwhoami / list_organizations / create_organization / list_projects / list_environments / list_members / invite_member (admin 以上)
組織の toolorganization_idcreate_app (admin 以上) / list_api_keys / revoke_api_key (developer 以上、confirm: true で実行) / get_account / create_checkout・get_billing_portal (owner)
環境の toolapp_idget_app / list_users / list_credentials / list_audit / get_stats (viewer 以上)、update_app / set_resident_key / create_api_key / rename_credential / revoke_credential / revoke_user / issue_recovery_token (developer 以上)、delete_user (admin 以上)
ガイド・料金—get_integration_guide / list_plans

create_app で project_id を省くと、管理画面と同じく本番とテストの 2 つの環境を持つプロジェクトを作ります (テスト環境は rp_id localhost、返り値の test_app_id)。project_id と environment (live / test) を渡すと、そのプロジェクトに無い環境を足します。平文の API キーは create_api_key の応答にだけ含まれます。テスト環境のキーは sk_test_ で始まり、テスト環境の利用者は料金の対象外です。操作は環境の監査ログに、操作した人とクライアントの名前とともに残ります。利用者のパスキー登録とログインはブラウザの認証器が要るため tool にはなく、ガイドに沿ってコードを書いて組み込みます。

許可したアプリは、管理画面の「アカウントの設定 → 接続中のアプリ」に並び、そこから取り消せます。アクセストークンは 1 時間、リフレッシュトークンは 30 日で、使うたびに新しいものに換わります。一度使ったリフレッシュトークンがもう一度使われると、その接続を取り消します。

OAuth のエンドポイント

GET  https://api.sealgate.jp/.well-known/oauth-protected-resource/v1/mcp   保護されたリソースのメタデータ (RFC 9728)
GET  https://app.sealgate.jp/.well-known/oauth-authorization-server        認可サーバーのメタデータ (RFC 8414)
POST https://app.sealgate.jp/api/oauth/register    動的クライアント登録 (RFC 7591、公開クライアント)
GET  https://app.sealgate.jp/api/oauth/authorize   認可 (code、PKCE は S256 だけ、resource は MCP の URL)
POST https://app.sealgate.jp/api/oauth/token       authorization_code / refresh_token
POST https://app.sealgate.jp/api/oauth/revoke      取り消し (RFC 7009)

Authorization の無い POST /v1/mcp → 401
WWW-Authenticate: Bearer resource_metadata="https://api.sealgate.jp/.well-known/oauth-protected-resource/v1/mcp"

MCP-Protocol-Version は 2025-11-25 / 2025-06-18 / 2025-03-26 / 2024-11-05 に対応します。

API キーで使う (OAuth に対応していないクライアント)

管理画面で発行した API キーを Authorization: Bearer に付けて同じ URL を呼ぶと、キーの環境 (アプリ) の tool を使えます。app_id・organization_id は要りません。使える tool は、環境の設定 (rp_id と許可オリジンを除く)・その環境の API キーの一覧・利用者とパスキー・復旧トークン・監査ログ・数字・組織の状態 (get_account) です。アプリの作成、API キーの発行と失効、許可オリジンの変更、お支払いは、ログイン (OAuth) で繋いだ MCP か管理画面で行います。tool の引数 api_key でキーを渡す形は廃止しました (チャットに API キーが残るため)。

登録は https://api.sealgate.jp/v1/mcp/signup (認証なし) の signup です。メールアドレスに確認のリンク (24 時間有効) を送り、開いて確認すると組織ができます。API キーは返しません。確認のあと、管理画面で API キーを発行してください。この入口では get_integration_guide と list_plans も使えます。

ブラウザ SDK

https://sealgate.jp/sdk/v1.js (ESM) を読み込むか、npm の sealgate-browser (同じ内容、型付き) を使います。optionsUrl と verifyUrl は開発者のサーバー側の URL で、SDK は kind (register | login) を付けて POST します。

npm install sealgate-browser

import { SealGate, SealGateError } from 'sealgate-browser';
import { SealGate } from 'https://sealgate.jp/sdk/v1.js';

const sg = new SealGate({
  optionsUrl: '/api/passkey/options',
  verifyUrl: '/api/passkey/verify',
});

// 登録 (ログイン済みの画面で。2 本目以降も同じ)
await sg.register({ name: 'この端末' });

// ログイン
const result = await sg.login();          // 端末の鍵から選ぶ
const result2 = await sg.login({ external_id: 'user-123' });

入力欄の自動入力 (conditional UI)

mediation: 'conditional' を指定すると、認証ダイアログを出さずに、ユーザー名の入力欄の自動入力にパスキーを出します。入力欄に autocomplete="username webauthn" が必要です。開発者のサーバーは external_id を付けずに /v1/login/options を呼びます。利用者が候補を選ぶまで Promise は解決しないため、画面の表示時に呼び出します。

<input name="username" autocomplete="username webauthn" />

if (await SealGate.conditionalMediationAvailable()) {
  sg.login({}, { mediation: 'conditional' })
    .then(onSignedIn)
    .catch((e) => { if (e.code !== 'aborted') console.error(e); });
}

// ボタンでのログインに切り替えるときや、SPA で画面を離れるとき
SealGate.abort();

SealGate.supported() でパスキー対応、SealGate.platformAuthenticatorAvailable() で生体認証の有無、SealGate.conditionalMediationAvailable() で自動入力への対応を確認できます。失敗は SealGateError で投げます。code は cancelled (利用者が閉じた・時間切れ)、aborted (SealGate.abort() などで中断)、already_registered (この認証器に同じ利用者の鍵がある)、unsupported (非対応、または自動入力の入力欄が無い)、http_error、API のエラーコードのいずれかです。

サーバー側の実装例

SDK からの POST を受けて SealGate に中継し、verify が通ったらセッションを発行します。

Node.js (Next.js Route Handler)

// POST /api/passkey/options と /api/passkey/verify を 1 つで受ける例
const BASE = 'https://api.sealgate.jp/v1';

export async function POST(req, { params }) {
  const step = params.step;                 // 'options' | 'verify'
  const body = await req.json();            // { kind, ... } SDK が送る
  const user = await currentUser(req);      // 開発者側のログイン状態

  const payload = step === 'options'
    ? body.kind === 'register'
      // 2 本目以降の追加は signed_in: true。ログイン済みかはサーバーのセッションで決める
      ? { external_id: user.id, display_name: user.email, signed_in: true }
      : { external_id: body.external_id }   // 省略可 (自動入力では付けない)
    : { challenge_id: body.challenge_id, response: body.response };

  const res = await fetch(`${BASE}/${body.kind}/${step}`, {
    method: 'POST',
    headers: { authorization: `Bearer ${process.env.SEALGATE_API_KEY}`, 'content-type': 'application/json' },
    body: JSON.stringify(payload),
  });
  const json = await res.json();

  if (step === 'verify' && body.kind === 'login' && res.ok) {
    await createSession(json.external_id);  // 開発者側のセッション
  }
  return Response.json(json, { status: res.status });
}

curl

curl -X POST https://api.sealgate.jp/v1/login/options \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"external_id":"user-123"}'

エラー

形は { "error": { "code", "message" } } です。

codeHTTP意味
invalid_request400パラメータの不足や形式の誤り
invalid_api_key401API キーが無いか、失効している
verification_failed401署名の検証に失敗した。名乗った利用者と鍵が一致しない場合も含む
credential_revoked403失効した鍵でのログイン
origin_not_allowed403アプリの許可オリジン以外からの応答
recovery_not_allowed403鍵を持つ利用者の追加登録に signed_in も recovery_token も無い。復旧トークンが無効か期限切れ。復旧が無効なアプリで復旧を要求した
mau_limit402支払い方法を登録していない組織が月の MAU の上限 (1,000) に達した。今月はじめてログインする利用者だけが止まる
not_found404利用者や鍵が無い
challenge_expired410challenge が期限切れ (5 分)、使用済み、または不明
rate_limited429短時間に呼びすぎ
internal_error500SealGate 側の障害

API キーの発行は管理画面から。ご利用の開始はお問い合わせから。