integration-development について
Membrane を使った連携機能の開発者向けガイドです。認証、接続、外部アプリのアクション実行を扱い、シングルテナントとマルチテナントの設計や複数の実行方法を説明します。
主な機能
- 01Membrane 連携の 5 段階フロー
- 02シングル・マルチテナント設定
- 03接続の作成とライフサイクル管理
- 04意図に基づく連携とアクションの検索
- 05API、コード、アクション ID による実行
活用シーン
- 01チーム共用アカウントの接続作成
- 02ユーザーごとの外部アカウント接続
- 03適切な連携アクションの検索と実行
MCP 対応
MCP セッションでの認証状態を説明し、接続、連携、アクションの検索に対応する MCP ツールを示します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ membranehq/ agent-skills --skill integration-developmentインストール後、Claude Code で/integration-developmentを入力して読み込まれたか確認します。
詳細
インテグレーション開発
インテグレーションの仕組み — 5ステップのメンタルモデル
Membrane を利用する機能はすべて、同じ一連のステップをたどります。 ステップ 0〜3 が基本の流れです。ステップ 4 は、アプリが特定のベンダーだけに限定されない場合に進む、機能拡張のステップです。
Step 0: Authenticate yourself — you, the developer; via MCP or `membrane` CLI
Step 1: Authenticate your code — sign JWTs in your backend
↳ single-tenant vs multi-tenant decided HERE
Step 2: Create connections — `membrane.ui.connect()` in your app UI
Step 3: Use connections — call actions on the external app
↳ discover via intent search when you don't know what to call
↳ dispatch: inline api/code | catalog id | workspace key
Step 4 (advanced): Universal — app-agnostic code via universal actions + cross-connection logic
MCP セッションではステップ 0 はすでに完了しています — membrane status で確認できます。
最も重要な選択、つまりアプリが共有の外部アカウントを 1 つ提供するのか(シングルテナント:チームの CRM、社内 Slack)、それとも多くのエンドユーザーに提供するのか(マルチテナント:各ユーザーが自分の Gmail、Linear などを接続するのか)は、ステップ 1 で行います。この選択によって、バックエンドが署名する JWT に何を含めるかが決まり、それが後続するすべての接続とアクションのスコープを決定します。ステップ 1 以降はすべて共通です。同じ membrane.ui.connect() 呼び出し、同じ /act ディスパッチを使います。
コードを書く前に — 一般的な調査フロー
ほとんどの場合、次の 4 つのコマンドで開始できます。
# 1. Populate env with real credentials.
membrane credentials --shell > .env.local
# 2. Find the app you're integrating with (returns externalAppId).
membrane external-app list --search <app-name>
# 3. List catalog actions for that app.
membrane action list --externalAppId <id>
# 4. Check existing connections in the current tenant.
membrane connection list
CLI クイックリファレンス。
- JWT を発行する:
membrane token(ユーザーごとの場合は--tenantKey <key>、POST /actionsやその他のマネージャー操作には--managerを追加します)。membrane --helpまたはmembrane <command> --helpで、利用可能なコマンドの一覧を確認できます。
接続を作成する際に connection list で一致するものがない場合、最初の接続では integrationKey(ワークスペース内でのみ有効)ではなく、connectorKey(カタログレベルで常に解決可能)を渡してください。
ステップ 0:認証状態を確認する
Membrane のセッションが有効であることを確認します。
membrane status
「Logged in」が緑色で表示され、ワークスペースも表示されていれば準備完了です。「Not logged in」が赤色で表示された場合は、認証してください。
npx @membranehq/cli login
サインイン用のブラウザーウィンドウが開きます。認証情報は ~/.membrane/credentials.json に保存され、それ以降の membrane コマンドでは毎回再利用されます。セッションの有効期限が切れた場合は、再度実行してください。
ステップ 1:コードを認証する
バックエンドから Membrane への各リクエストには、Membrane トークンが含まれます。これは、ワークスペースシークレットで署名された、有効期間の短い HS256 JWT です。tenantKey によって、リクエストが操作する接続の分離されたスコープが決まります。
ペイロードの形式
workspaceKey— リクエストの実行先となる Membrane ワークスペースです。tenantKey— ワークスペース内で接続を分離するスコープです。異なるテナント間で互いの接続を参照することはできません。
これらに加えて、署名に使用するワークスペースシークレットとAPI URLが必要です。
tenantKey の選び方:
| シングルテナント | マルチテナント | |
|---|---|---|
| 使用するケース | 共有の外部アプリ用アカウントが 1 つ(チームの CRM、Slack) | エンドユーザーごとに独自の外部アプリ用アカウントがある(Gmail、Linear) |
JWT 内の tenantKey | 固定値 — 例:process.env.MEMBRANE_TENANT_KEY | サインインしたユーザーの固定 ID |
| 接続キーの形式 | 固定値 — 例:hubspot-prod | ユーザー ID から生成 — 例:gmail-${userId} |
| 接続 UX(ステップ 2) | 管理者ページ。アプリごとに 1 回クリックする | ユーザーごとのボタン。各ユーザーが自分のアカウントを接続する |
値を取得する
membrane credentials --json
# { "apiUri": "...", "workspaceKey": "...", "workspaceSecret": "...", "tenantKey": "..." }
.env ファイル用のシェルエクスポート形式:membrane credentials --shell。
トークンに署名する
標準の HS256 JWT で、リクエストごとに有効期間を短く設定して署名します。
import jwt from 'jsonwebtoken'
// tenantKey source:
// single-tenant → process.env.MEMBRANE_TENANT_KEY
// multi-tenant → the signed-in user's stable id
const token = jwt.sign(
{ workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY, tenantKey },
process.env.MEMBRANE_WORKSPACE_SECRET,
{ algorithm: 'HS256', expiresIn: '5m' },
)
// Use as Authorization: Bearer <token>, or hand to
// <MembraneProvider fetchToken={…}> on the frontend.
シェルでのテストには、membrane token でテナントスコープのトークンを発行できます(ユーザーごとの場合は --tenantKey <key> を追加します)。MCP では同等の機能として get-membrane-token を使います。
ワークスペースマネージャートークン
テナントスコープのトークンは、act、一覧表示、アプリのランタイムが実行するすべての処理をカバーします。次の操作には マネージャー トークン(エンジンの workspaceAsManager 認証レイヤーのスコープ)が必要です。
POST /actions/PATCH /actions/<id>/DELETE /actions/<id>(ワークスペースレベルで再利用可能なアクション)PATCH /integrations/<id>/parameters(OAuth 認証情報、スコープ)POST /import/GET /export(ワークスペース設定の往復)
membrane token --manager で発行するか、直接署名します。
const managerToken = jwt.sign({ workspaceKey, isAdmin: true }, workspaceSecret, { algorithm: 'HS256', expiresIn: '5m' })
ステップ 2:接続を作成する
接続は、1 つの外部アプリへの認証済みリンクです。すべての
/act 呼び出しは、接続の認証を経由します。
フロントエンドからの membrane.ui.connect(…) は、ユーザー向けの接続フローです。OAuth ポップアップを開き、状態を追跡し、ユーザーが完了すると解決します。
接続のライフサイクル
| 操作 | CLI | MCP ツール |
|---|---|---|
| 既存の接続を一覧表示 | membrane connection list | list-connections |
| アプリを検出 | membrane integration list --search "<q>" | list-integrations |
| 作成 | membrane.ui.connect(…)(本番環境)/membrane connect …(開発環境) | connect |
| 再接続 | membrane connect --connectionId <id> | { connectionId } を指定して connect |
| 削除 | membrane connection delete <id-or-key> | delete-connection |
接続可能な対象を見つける
list-integrations は、ワークスペースのインテグレーションに加え、カタログのコネクターと外部アプリを検索します。各結果には connectorId、
integrationId、externalAppId が含まれます。これらのいずれかを connect に渡します。一致するものがない場合、connect は自由記述の intent も受け付け、その場でコネクターを構築します。この方法で初めて接続する場合は、通常より時間がかかることがあります(BUILDING フェーズが発生します。失敗した場合は CONFIGURATION_ERROR/
SETUP_FAILED を確認してください)。
接続を作成する
connect に次のいずれかを渡します。
connectorKey/connectorId— カタログのコネクター(インテグレーションがまだない新規ワークスペースでも機能します)。integrationKey/integrationId— 既知のインテグレーション。intent— 自由記述。サーバーがコネクターを選択または構築します。
任意項目として、安定した検索に使う独自の connectionKey を指定できます。指定しない場合は自動生成されます。
connect は接続(id、key、state と、ユーザーの認証中は clientAction.uiUrl)を返します。CLI は state: READY になるまで処理をブロックします。MCP では、接続パネルにフローが表示されます。
再接続
既存の connectionId を指定して connect を呼び出すと、新しい接続を作成せずに OAuth を再認証できます。act 呼び出しで切断された接続のエラーが返された場合に使用します。
削除(アーカイブ)
delete-connection/membrane connection delete はレコードをアーカイブします。アーカイブされた接続は list-connections に表示されなくなり、act でも使用できなくなります。
インテグレーションをカスタマイズする
デフォルトでは、インテグレーションは Membrane の共有 OAuth クライアントを使用します。独自の OAuth 認証情報を指定したり、接続フォームのフィールドを調整したり、インテグレーションごとの動作を変更したりするには、カスタマイズしてください。詳細は integration-catalog を参照してください。
接続 UI — シングルテナント
共有アカウントを 1 つ使用し、管理者用の接続ページを 1 つ設けます。接続キーは固定です。
import { MembraneClient } from '@membranehq/sdk'
// The backend mints a token (step 1) and exposes it at /api/membrane-token
// as { token, apiUri }.
const { token, apiUri } = await (await fetch('/api/membrane-token')).json()
const m = new MembraneClient({ token, apiUri })
await m.ui.connect({ connectorKey: 'hubspot', connectionKey: 'hubspot-prod' })
Connect UI — マルチテナント
サインインした各ユーザーが、それぞれ自分のアカウントを接続します。接続キーはユーザー ID から導出されます。
@membranehq/react を使う場合 — アプリ全体を一度だけ MembraneProvider でラップします。
import { MembraneProvider, useMembrane } from '@membranehq/react'
;<MembraneProvider
apiUri={process.env.NEXT_PUBLIC_MEMBRANE_API_URI}
fetchToken={async () => (await (await fetch('/api/membrane-token')).json()).token}
>
{children}
</MembraneProvider>
// In any component:
const m = useMembrane()
m.ui.connect({ connectorKey: 'gmail', connectionKey: `gmail-${userId}` })
プレーンな JS — 同じパターンで、ユーザー単位の接続キーを使用します。
await m.ui.connect({ connectorKey: 'gmail', connectionKey: `gmail-${userId}` })
トークンルートに関する注意事項:
{ token, apiUri }を返すGET /api/membrane-tokenがあれば、どのサーバーフレームワークでも動作します。fetchTokenはリクエストごとに呼び出されるため、トークンは短命に保たれます。- Vite の開発専用アプリの場合:ステップ 1 と同じ JWT に署名する開発ミドルウェアを追加します。
loadEnv(mode, …)を使うと、ミドルウェアからMEMBRANE_*環境変数にアクセスできます。 m.ui.connect(...)はブラウザーから呼び出す必要があります。OAuth ポップアップが開くためです。サーバー側のリダイレクトや CLI コマンドでは代用できません。
ダッシュボードのカタログページ
ユーザーが多数の連携を管理する、設定画面スタイルの UI です。@membranehq/react のフックを使用します。
useIntegrations() // → { items: Integration[], loading, loadMore, loadingMore, refresh }
useConnections() // → { items: Connection[], loading, refresh }
useMembrane() // → MembraneClient (.ui.connect(…), .connection(id).archive(), …)
type Connection = {
id: string
name: string
integrationId: string
integration?: { key: string; name: string; logoUri?: string }
disconnected: boolean
state: 'READY' | 'BUILDING' | 'DISCONNECTED' | 'ERROR'
}
type Integration = {
id: string
key: string
name: string
logoUri?: string
}
接続するために必要な、Membrane の一意な呼び出しは 4 つです。
// Connect a new integration the user picked from the available list:
m.ui.connect({ integrationKey: i.key })
// Reconnect a disconnected/errored connection:
m.ui.connect({ connectionId: c.id })
// Disconnect (archive):
m.connection(c.id).archive()
UI では state 列挙型が重要です。BUILDING はユーザー認証の完了から、Membrane がコネクターを確定するまでの期間です(初回接続時には数秒かかることがあります)。接続済み/未接続を真偽値だけで表示すると、BUILDING や ERROR も「接続済み」と表示されてしまいますが、実際はそうではありません。
ステップ 3:接続を使用する — アクションを呼び出す
/act は、処理を実行するための単一のエンドポイントです。ディスパッチ方式は 4 種類あり、1 回の呼び出しで使用できるのはそのうち 1 つだけです。
api— 接続の認証レイヤーとベース URL を通じて、インライン HTTP リクエストを実行します。デフォルトの構成要素です。code— 認証済みのmembraneクライアント、connection、integrationがあらかじめ設定されたサンドボックスで、JS スニペットを実行します。複数ステップの処理を組み合わせる場合に適しています。id— 特定のアクション(カタログまたはワークスペースローカル)を指定します。action list --externalAppIdで取得したカタログアクションにはidだけが含まれます(keyフィールドは空です)。key— ワークスペースローカルアクションの安定した識別子です。カタログアクションにはキーがなく、ワークスペースローカルアクションにはidとkeyの両方があり、どちらでも使用できます。
api と code には接続(connectionKey または connectionId)が必要です。key/id はアクション自体のスコープを通じて処理されます。
アクションを実行する
membrane act に --api、--code、--key、--id のいずれかを指定し、接続と入力のフラグを追加します。
レスポンス:{ output, actionRunId }。失敗した場合もレスポンスには actionRunId が含まれます。デバッグのため、これを実行ログに渡してください。
意図からアクションを見つける
実行したいことは分かっていても方法が分からない場合は、意図に基づいてランク付けされたアクションを一覧表示します。
membrane action list --connectionKey <key> [--intent "<text>"]
ディスパッチの例 — id によるカタログアクションの指定
一般的な流れは、探索ワークフローのステップ 3 で、id、name、inputSchema、config.request を持つカタログアクションの一覧を取得することです。該当するアクションを id で使用します。ベンダー固有のリクエスト形式、認証スコープ、再試行は Membrane が処理します。該当するカタログアクションがない場合に限り、インラインの api: にフォールバックしてください。
// /act body:
{
id: GMAIL_SEND_MESSAGE_ACTION_ID, // paste from `membrane action list --externalAppId`
connectionKey: `gmail-${userId}`, // or `hubspot-prod` for single-tenant
input: { to, subject, body }, // matches the action's inputSchema
}
// On failure, detect disconnected connection and forward actionRunId:
const needsReconnect = res.status === 401 || payload?.code === 'CONNECTION_DISCONNECTED'
// Return { error: payload, actionRunId: payload?.actionRunId, needsReconnect }
該当するカタログアクションがない場合のインライン api::
{
connectionKey,
api: {
method: 'POST',
path: '/<vendor-endpoint>',
body: { … },
},
}
ワークスペースローカルの再利用可能なアクションに移行する
同じディスパッチ形式が複数のルートで繰り返される場合は、安定した key を持つワークスペースローカルアクションに切り出します。マネージャートークンを使って、一度だけ POST /actions を実行します。
作成方法は 2 つあります。
- 意図による作成 — Membrane Agent がアクションを構築して登録します。
- 明示的な仕様による作成 —
typeとconfigを指定します。
# by intent
membrane action create --intent "<text>" --connectionKey <key>
# by explicit spec
membrane action create --key <key> --type <type> --config '<json>' --integrationKey <key>
# update (merge) / replace (whole) / delete
membrane action update <identifier> --data '<json>'
membrane action replace <identifier> --data '<json>'
membrane action delete <identifier>
POST /actions の本文形式(明示的な仕様):
{
"key": "create-lead",
"name": "Create Lead",
"type": "api-request-to-external-app",
"externalAppId": "<app-id>",
"config": {
"request": { "method": "POST", "path": "/<vendor-endpoint>" }
},
"inputSchema": { "type": "object", "required": ["…"], "properties": { "…": "…" } }
}
{ id, key, state: 'READY', inputSchema, outputSchema, … } が返されます。実行時には { key: '<your-key>', connectionKey, input } で呼び出します。
アクションのスコープは、設定するフィールドによって決まります。
connectionKey/connectionIdを設定 → 接続レベル(1 つの接続)。integrationKey/integrationIdを設定し、接続を指定しない → 連携レベル(その連携上のすべての接続で共有)。この方法でユニバーサルアクションを連携ごとにカスタマイズできます。同じkeyを使いながら、フィールドマッピングや前後処理フックを変えられます。- どちらも設定しない → ユニバーサルアクション(ステップ 4 を参照)。
POST /actions にはマネージャートークンが必要です。テナントトークンでは 403 が返されます。
失敗したアクションをデバッグする
すべての act レスポンスには actionRunId が含まれています。実行ログに渡してください(--details / includeDetails: true を指定すると、外部アプリとの生の HTTP 通信も取得できます)。
membrane action-run-log get <actionRunId> --details
ステップ 4(上級):ユニバーサル連携
ステップ 0~3 では、コードがどの外部アプリと通信しているかを把握していることを前提としています。ステップ 4 は、別の形に対応します。ユーザーがどのベンダーに接続しても、アプリが価値を提供する場合です。たとえば、Gmail、Outlook、SendGrid で動作する「メール送信」機能や、Salesforce、HubSpot、Pipedrive を一律に扱う CRM 同期レイヤーなどです。
これは異なるディスパッチ経路です。ベンダーに依存しない動作が、製品の第一級の要件である場合にのみ利用してください。
ユニバーサルアクション
ユニバーサルアクションは、externalAppId も integrationKey も指定せずに登録します。「連絡先を作成」「メッセージを送信」「請求書を一覧表示」といったインテントを指定し、それを実装するすべての連携で共有される入出力スキーマを用意します。ディスパッチは、呼び出し元の接続が属する連携を通じて行われます。
# Create by intent — Membrane builds a shared schema at workspace scope.
membrane action create --intent "send an email" --key send-email
ディスパッチでは externalAppId を指定しません。アクションは接続に応じて動作します。
await act({
key: 'send-email',
connectionKey: userPickedConnection, // Gmail, Outlook, SendGrid, etc.
input: { to, subject, body },
})
ベンダー固有の調整(フィールド名の違いや、特定ベンダー向けの追加フックなど)は、連携レベルで上書きできます。同じ key で integrationKey: "gmail" を指定した 2 つ目のアクションを作成してください。Gmail の接続が使われると、ランタイムのディスパッチによって上書きが自動的に適用されます。
複数接続にまたがるロジック
Membrane は、複数接続にまたがるアクションを直接モデル化しません。処理が複数の接続にまたがる場合は、次の方法を使います。
- バックエンドでオーケストレーションする:接続ごとに個別の
/act呼び出しを順番に実行します。 code:ディスパッチ:1 つの接続を事前に設定した短い JS スニペットを使い、スニペット内でmembraneクライアントを介して 2 つ目の接続にアクセスします。
ユニバーサルフロー向けのワークスペース設定
マネージャートークンを使って PATCH /workspaces/:id から切り替えます。
useMembraneUniverse— アクションの検索対象を、ワークスペースの連携だけでなく、すべてのカタログ連携にまたがるユニバーサルアクションに広げます。autoGenerateIntegrationsFromConnectors— 新しいコネクターが使われたときに、必要に応じて連携を作成します。ユニバーサルな UX に必要です。ユーザーが任意のコネクターを選ぶと、ユニバーサルアクションをディスパッチするための連携が作成されます。autoGenerateIntegrationsFromExternalApps— 必要に応じて外部アプリのカタログから連携を生成します。
アプリのユーザーに表示される内容が広がるため、これらはオプトイン方式です。
ステップ 3 のままにする場合
- ベンダー固有のヘッダーや例外ケース → ステップ 3 のカタログ
idまたはインラインのapi:。 - 同じアプリへの呼び出しを繰り返す場合 → ステップ 3 の
keyを使って、ワークスペース内で再利用可能なアクションに移行します。 - 単一ベンダー向けの製品で、期限が迫っている場合 → ユニバーサル対応は設計作業が増えるため、後で検討してください。
エラーからの復旧
/act が接続切断エラーを返した場合は、connectionId を UI(または運用チャンネル)に渡し、ユーザーに再接続してもらいます。新しい接続は作成しないでください。既存の接続は OAuth の再認証が必要なだけです。
if (err.status === 401 || err.payload?.code === 'CONNECTION_DISCONNECTED') {
// Interactive path: return 409 with the connectionId; frontend
// calls m.ui.connect({ connectionId }) and retries after the user
// completes the popup.
//
// Unattended workload (cron, worker, sync): log the connectionId
// and actionRunId, emit an alert, and bail. The next run picks up
// once an admin has reconnected via the Settings page.
}
公開されているツール以外 — 生の API
Membrane REST API がサポートする機能は、get-membrane-token でトークンを発行し、エンドポイントを直接呼び出すことでコードから利用できます。第一級のツールではカバーされていない主なエンドポイントは次のとおりです。
- アーカイブ済みのアクション/接続を復元する —
POST /actions/:id/restore、POST /connections/:id/restore。 - アクション全体を置き換える —
PUT /actions/:id(MCP のupdate-actionはマージのみを行います)。 - 接続にパッチを適用する —
PATCH /connections/:id。 - 接続の認証情報を更新する —
POST /connections/:id/refresh。 - 連携を確認/カスタマイズする —
GET /integrations/:id、PATCH /integrations/:id。integration-catalogを参照してください。
API リファレンス全文:
docs.getmembrane.com/reference。
特定のエンドポイントを呼び出す前に、search-docs で検索してください。
フロントエンドのセットアップ
ブラウザーコードで Membrane を初期化する方法です。React アプリ、その他のブラウザーフレームワーク、埋め込み UI が対象です。接続ダイアログや再接続フローを表示するものは、OAuth の実行に実際のブラウザーが必要なため、ここに含まれます。
重要な原則: ブラウザーからワークスペースシークレットを参照できないようにしてください。サーバーでトークンに署名し、fetchToken形式のコールバックを使ってフロントエンドに渡します。トークンを発行するサーバールートについては、backend-setupを参照してください。
Next.js / React
@membranehq/reactをインストールします。アプリをMembraneProviderでラップし、バックエンドにアクセスするfetchToken関数を渡します。
// app/layout.tsx
import { MembraneProvider } from '@membranehq/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
<MembraneProvider
fetchToken={async () => {
const res = await fetch('/api/membrane-token')
return (await res.json()).token
}}
>
{children}
</MembraneProvider>
</body>
</html>
)
}
その後、プロバイダーツリー内の任意の場所で次のようにします。
import { useMembrane } from '@membranehq/react'
function ConnectButton() {
const membrane = useMembrane()
return <button onClick={() => membrane.ui.connect({ integrationKey: 'hubspot' })}>Connect HubSpot</button>
}
Next.js の注意点: next.config.tsにtranspilePackages: ['@membranehq/react', '@membranehq/sdk']を追加してください。
React 以外のブラウザーコード
@membranehq/sdkを直接使用します。同じくバックエンドからトークンを取得する方法で、fetchToken(または静的なtoken)をMembraneClientに渡します。
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({
fetchToken: async () => {
const res = await fetch('/api/membrane-token')
return (await res.json()).token
},
})
await membrane.ui.connect({ integrationKey: 'hubspot' })
Vue、Svelte、Angular、プレーンな JavaScript でも同じ形です。fetchTokenが指定されている場合、SDK がトークンを自動的に更新します。
フロントエンド SDK を呼び出すタイミング
- ユーザー操作が必要な UI フロー —
membrane.ui.connect(...)、再接続ダイアログ、接続管理画面などです。これらはブラウザーで実行する必要があります。 - サインイン中のユーザーに紐づく読み取り専用の読み取り処理 — テナント自身の接続一覧の取得や、テナント自身のアクション結果の読み取りなどは、トークンがすでにテナントのスコープに制限されているため、フロントエンドから実行しても問題ありません。
- その他すべて — アプリに代わって行う書き込み、バックグラウンド同期、ワークスペース全体の管理などはバックエンドで行います。
backend-setupを参照してください。
関連項目
バックエンドのセットアップ
サーバーコードで Membrane を初期化する方法です。バックエンドルート、バックグラウンドワーカー、cron ジョブ、CLI、単発のスクリプトが対象です。ワークスペースシークレットを安全に保持でき、ユーザー向けの UI フローが実行されない場所で使用します。
ここでは、frontend-setupが参照する/api/membrane-tokenルートも実装します。フロントエンドがテナントのスコープに制限された JWT をバックエンドに要求し、バックエンドが署名して返します。
重要な原則: ワークスペースシークレットはサーバーに保持してください。ブラウザーバンドル、モバイルアプリ、ユーザーが実行するデスクトップアプリ、またはユーザーがバイナリーを調査できる場所には埋め込まないでください。
代表的な 2 つのフロー
- 認証情報ベースのクライアント —
workspaceKey、workspaceSecret、tenantKeyをMembraneClientに渡します。トークンの発行と更新はクライアントが行います。ワーカー、スクリプト、CLI、シングルテナントのバックエンドなど、長時間実行され、一度に 1 つのテナントを扱うコードに最適です。 - トークンベースのクライアント/生の HTTP — 自分で JWT を発行し、
MembraneClient({ token })に渡すか、生のfetchでAuthorization: Bearer <token>ヘッダーに指定します。マルチテナントのリクエストハンドラー、/api/membrane-tokenルート、JavaScript 以外のサービスなど、明示的な制御が必要な場合に最適です。
どちらのフローも同じ API と通信します。トークンのライフサイクルをどの程度制御する必要があるかに応じて選択してください。
ワークスペース認証情報を使った MembraneClient の使用
スクリプト、ワーカー、シングルテナントのバックエンドにそのまま導入できます。
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({
workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
workspaceSecret: process.env.MEMBRANE_WORKSPACE_SECRET!,
tenantKey: process.env.MEMBRANE_TENANT_KEY!, // or the end-user id
apiUri: process.env.MEMBRANE_API_URL,
})
const connections = await membrane.connections.find()
SDK は呼び出しごとに HS256 トークンを発行し、有効期限が切れる前に更新します。マルチテナントのバックエンドでは、リクエストごとにtenantKeyを切り替えてください(スコープを設定したクライアントを構築します)。
トークンを発行して API を直接呼び出す
細かな制御が必要な場合、JavaScript 以外のランタイムを使用している場合、またはフロントエンド用の/api/membrane-tokenルートを作成する場合は、この方法を使用します。
import jwt from 'jsonwebtoken'
function membraneTokenForTenant(tenantKey: string) {
return jwt.sign(
{
workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
tenantKey,
},
process.env.MEMBRANE_WORKSPACE_SECRET!,
{ algorithm: 'HS256', expiresIn: 3600 },
)
}
// Example: Next.js API route that vends tokens to the browser
export async function GET(req: Request) {
const tenantKey = await resolveTenantForUser(req) // your auth system
return Response.json({ token: membraneTokenForTenant(tenantKey) })
}
// Or: call Membrane directly from the server
async function listConnections(tenantKey: string) {
const token = membraneTokenForTenant(tenantKey)
const res = await fetch(`${process.env.MEMBRANE_API_URL}/connections`, {
headers: { Authorization: `Bearer ${token}` },
})
return res.json()
}
JavaScript ランタイム以外を使用している場合、get-membrane-tokenツールにペイロードの詳細なリファレンスと、Python、Go、Ruby、Java、PHP での署名例があります。
スクリプトと CLI
バックエンドルートと同じ方法です。ローカルの環境ファイル(またはget-credentials)からワークスペースの認証情報を取得し、トークンを発行して API を呼び出します。シングルテナントのスクリプトでは、コンソールセッションに紐づくテナントキーをハードコードできます。これは、get-credentialsがデフォルトで返すテナントです。
// scripts/sync-hubspot.ts
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({
workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!,
workspaceSecret: process.env.MEMBRANE_WORKSPACE_SECRET!,
tenantKey: process.env.MEMBRANE_TENANT_KEY!,
})
const contacts = await membrane.connection('hubspot-prod').action('list-contacts').run({})
関連項目
- 認証
- テナント
get-credentialsツール — 現在の環境のworkspaceKey、workspaceSecret、tenantKey、apiUrlを返します。get-membrane-tokenツール — すぐに使用できる署名済み JWT を返します。
インテグレーションカタログ
ワークスペースのインテグレーションカタログ(実行時に
integrationKey で参照する、製品またはスクリプトで使用するインテグレーション群)の
セットアップと設定方法です。実行時ではなく、ビルド時または管理者による設定時に行う
作業です。
実行時の UI パターン
製品にインテグレーションページを構築する場合、実行時の標準的な流れは次のとおりです。
- 現在のテナントの
connectionsを取得します - 利用可能な
integrationsを取得します - 接続済みのアカウントと、ユーザーがまだ接続できるアプリを別々に表示します
membrane.ui.connect(...)を使用して、接続または再接続を開始します- 接続をアーカイブして切断します
import { useConnections, useIntegrations, useMembrane } from '@membranehq/react'
function IntegrationsPage() {
const membrane = useMembrane()
const { items: integrations = [] } = useIntegrations()
const { items: connections = [] } = useConnections()
const connectedIntegrationIds = new Set(connections.map((c) => c.integrationId))
const availableIntegrations = integrations.filter((i) => !connectedIntegrationIds.has(i.id))
return (
<>
{connections.map((connection) => (
<div key={connection.id}>
<span>{connection.name}</span>
{connection.disconnected ? (
<button onClick={() => membrane.ui.connect({ connectionId: connection.id })}>Reconnect</button>
) : (
<button onClick={() => membrane.connection(connection.id).archive()}>Disconnect</button>
)}
</div>
))}
{availableIntegrations.map((integration) => (
<button key={integration.id} onClick={() => membrane.ui.connect({ integrationKey: integration.key })}>
Connect {integration.name}
</button>
))}
</>
)
}
React 以外のフロントエンドアプリでも、同じ流れに従います。バックエンドから一時的な トークンを取得し、UI にインテグレーションと接続を一覧表示して、フロントエンドコードから 同じ接続/再接続フローを開始します。その UI の背後で動作するバックエンドルート、 ワーカー、または cron ジョブでは、サーバーサイド SDK を使わず、直接 API 呼び出しと 有効期間の短い Membrane トークンを使用します。
バックエンドのみのスキャフォールドですか?最小限のフロントエンドを追加してください。
UI のない API 専用の Express / Fastify / Next ルートハンドラーとして
スキャフォールドが生成された場合でも、接続フロー用のフロントエンド画面が必要です。
バックエンドから単一の静的 HTML ページを配信します。このページはアプリから
ユーザーごとのトークンを取得し、ブラウザー SDK を介して membrane.ui.connect を呼び出します。
<!doctype html>
<html>
<body>
<button id="connect-github">Connect GitHub</button>
<script type="module">
import { MembraneClient } from 'https://esm.sh/@membranehq/sdk'
const { token } = await (await fetch('/api/membrane-token')).json()
const m = new MembraneClient({ token, apiUri: 'https://api.getmembrane.com' })
document.getElementById('connect-github').onclick = () =>
m.ui.connect({ connectorKey: 'github', connectionKey: 'github-dev' })
</script>
</body>
</html>
/api/membrane-token ルートは、JWT に署名して(キットの「認証」を参照)、
{ token } を返す薄いバックエンドエンドポイントです。これがエンドユーザー向けの唯一の
正しい接続パターンです。エンドユーザー向けに CLI の手順を提供したり、バックエンドから
clientAction.uiUrl を返してユーザーが手動で貼り付けるようにしたりしないでください。
React フック/クライアントのクイックリファレンス
すべて @membranehq/react から利用できます。
// client — has all the methods you call imperatively
const m = useMembrane()
m.ui.connect({ connectorKey }) // new connection (catalog-level)
m.ui.connect({ integrationKey }) // new connection (workspace-level)
m.ui.connect({ connectionId }) // reconnect an existing connection
m.connection(id).archive() // disconnect
// hooks — SWR-style; re-render on changes
const { items: integrations, loading, error } = useIntegrations({ search?: string })
const { items: connections, loading, error } = useConnections({ integrationKey?: string })
// shape of a Connection
type Connection = {
id: string
key: string
name: string
integrationId: string
disconnected: boolean
state: 'READY' | 'DISCONNECTED' | 'ERROR' | 'BUILDING' | ...
}
// shape of an Integration
type Integration = {
id: string
key: string
name: string
logoUri?: string
}
アクションの位置づけ
アプリを接続した後、製品の機能で必要になるのは通常、アクションだけです。
apiまたはcodeを使って、バックエンド、ワーカー、cron ジョブからインラインのact呼び出しを実行します- これらのバックエンド呼び出し用のトークンは
get-credentialsまたはget-membrane-tokenで発行し、SDK の使用はフロントエンド層にとどめます - 安定したキー、スキーマ、または多くの呼び出し箇所で共有する抽象化が必要な場合に限り、 再利用可能なアクションを保存します
- 「同期」は多くの場合、独自のジョブランナーからスケジュールに沿ってアクションを 繰り返し実行するだけです
Membrane に同期オーケストレーション、イベント処理、またはテナントがカスタマイズ可能な マッピングをより多く任せたい場合は、データソース、フィールドマッピング、フローなどの より高度な Membrane プリミティブを使用します。
コネクターからインテグレーションを作成する
すべてのインテグレーションは、コネクター(Membrane がカタログに保持する
ベンダー固有のアダプター)に基づいています。インテグレーションを一度作成し、
その後は常に安定した integrationKey で参照します。
curl -X POST $API/integrations -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"key":"github","name":"GitHub","connectorKey":"github"}'
作成されたインテグレーションは、接続(connect --integrationKey github)と再利用可能なアクション(create-action --integrationKey github)を受け付けます。
インテグレーションのパラメーター
パラメーターは、OAuth クライアント認証情報、デフォルトのスコープ、API 固有の設定など、 インテグレーションの動作を設定します。これらはインテグレーションに属し、 インテグレーションを作り直さずに更新できます。
# fetch
curl -sG $API/integrations/<id>/parameters -H "Authorization: Bearer $TOKEN"
# set
curl -X PATCH $API/integrations/<id>/parameters -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"clientId":"...","clientSecret":"..."}'
自動生成されたインテグレーション
connectors=1 / externalApps=1(list-integrations を参照)を指定すると、ワークスペースは必要に応じてカタログからインテグレーションを生成します。エージェントは、専用のインテグレーションがまだないアプリにも接続でき、そのインテグレーションは自動的に作成されます。カスタム名、パラメーター、コネクタのバージョンをカタログに事前登録する場合に限り、明示的な POST /integrations が必要です。
最初の接続は通常の接続フローより時間がかかる場合があります。Membrane がカタログを検索したり、インテグレーションのレコードを作成したり、新しいコネクタを構築するエージェントを起動したりするためです。最初は BUILDING フェーズになることを想定してください。構築やセットアップのプロセスに失敗した場合は、通常 CONFIGURATION_ERROR または SETUP_FAILED が表示されます。再試行する前に、意図を明確にするか、正確なエラーを確認してください。
アーカイブと復元
インテグレーションは削除する代わりにアーカイブできます。DELETE /integrations/<id> はデフォルトでアーカイブします。アーカイブされたインテグレーションは、includeArchived=1 を設定しない限り list-integrations に表示されません。POST /integrations/<id>/restore で復元できます。
関連項目
- インテグレーション(API)
- 製品インテグレーション
- React SDK
- インテグレーションカタログガイド
- データインテグレーション
- インテグレーションの作成
- インテグレーションパラメーターの設定
- インポートとエクスポート
トラブルシューティング
Membrane で問題が発生した場合、ほとんどの場合、修正には2つのリソースを同時に確認する必要があります。失敗した act 呼び出しと、その呼び出しが経由する接続です。このセクションでは、それらにまたがるワークフローを説明します。各ツールのドキュメントでは、再接続の方法、ログのスキーマ、各状態の意味など、個別の仕組みを説明しています。
診断の流れ
アクション呼び出しが失敗した場合は、次の流れに従ってください。各ステップの結果を次のステップで使用します。
- レスポンスを読む。 すべての
actレスポンスには、成功時も 4xx の場合もactionRunIdが含まれています。これを取得してください。 - 実行ログを取得する。
includeDetails: trueを指定してget-action-run-logを呼び出します。これは、Membrane がマッピングした入力、出力、エラー、そして特に外部アプリとの生の HTTP 通信を確認する最も速い方法です。ほとんどの失敗は、ほかを調べなくてもここで原因を特定できます。 - ログの内容を分類する。 内容に応じて、該当する修正を行います。
- 認証エラー/切断 → 再接続します。
- 入力の形式が誤っている/フィールドが不足している →
actの入力を修正して再試行します(インラインのapi/codeが最小限のテスト環境になります)。 - 外部アプリがエラーを返した → ログでレスポンス本文を確認し、入力を修正するかエンドポイントを変更します。
- コネクタの設定が不足している →
integration-catalogを参照します。
- 可能な限り最小の呼び出しで再試行する。 失敗した同期処理や再利用可能なアクションを、まず単一のインライン
act(apiまたはcode)に縮小します。動作を確認したら、元の形に戻します。
ログの階層 — 上から順に確認する
Membrane は証拠を3つの階層で記録します。順番に確認してください。
- アクション実行ログ —
act呼び出しごとに1件のレコードです。入力、出力、エラー、状態の遷移など、Membrane の呼び出しに関する情報が含まれます。includeDetails: trueを指定すると、生の HTTP 通信も含まれます。まずここを確認してください。 - 外部 API ログ — ベンダーに送信されたリクエストとレスポンスの生データです。
includeDetailsを指定すると確認できます。アクション実行ログに「外部エラー」と表示され、ベンダーからの正確なレスポンス本文やステータスコードを確認する必要がある場合に使用してください。 - Webhook ログ — ベンダーから受信したペイロードの生データです。イベント駆動型のフローをデバッグする場合にのみ使用してください。同期
act呼び出しには関係ありません。
通常、階層2より下を調べる必要はほとんどありません。
症状 → ツールの対応表
| 症状 | 最初に行うこと |
|---|---|
act が actionRunId とともにエラーを返した | includeDetails: true を指定して get-action-run-log |
| 認証エラー/「connection disconnected」 | エラーが発生した connectionId を指定して connect |
接続が長時間 BUILDING のまま | 待ってから list-connections で最終状態を確認する |
接続が CONFIGURATION_ERROR になった | connect を再実行し、返されたエラーを確認する |
接続が SETUP_FAILED になった | コネクタをビルドできませんでした。別の intent を試す |
| 「Integration not found」/「no matching action」 | 再試行の前に list-integrations または list-actions |
| 再利用可能なアクションの出力が正しくない | まず act と api/code を使ってインラインで再構築する |
| プラットフォーム全体の問題と思われる(複数のテナント、Webhook) | 該当機能を search-docs で検索する |
関連項目
ツール
認証情報を取得する
membrane credentials
任意のバックエンドから Membrane と通信するために必要な認証情報を取得します。 バックエンドのルート、ワーカー、cron ジョブ、サーバーサイドスクリプトで使用してください。 フロントエンドコードでは、バックエンドから一時トークンを取得してから フロントエンド SDK を使用してください。
以下を返します。
- Workspace key — 接続先の Membrane ワークスペースです。
- Workspace secret — JWT の署名に使用します。サーバーサイドのみで管理してください。
- Tenant key — 呼び出しを、1 人のユーザーの接続セットに限定します。 テストではコンソールセッションのテナントを使用し、本番環境では 現在のエンドユーザーを一意に識別する値に置き換えてください。
- API URL — 現在の環境に対応する Membrane エンドポイントです。
後で 401 エラーが発生する場合、ワークスペースシークレットが ローテーションされた可能性があります。このツールを再度呼び出して、新しい値を取得してください。
コードでの使用
返された値を使って JWT を生成し、API 呼び出しを認証します。
# 1. Mint a token (HS256, 1-hour expiry)
TOKEN=$(node -e "
const jwt = require('jsonwebtoken');
console.log(jwt.sign(
{
workspaceKey: '<workspaceKey>',
tenantKey: '<tenantKey>',
name: 'Acme Inc'
},
'<workspaceSecret>',
{ algorithm: 'HS256', expiresIn: 3600 }
))
")
# 2. Use it
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"
JWT ライブラリがあれば、どの言語でも使用できます。ペイロードは
{ "workspaceKey": "<workspaceKey>", "tenantKey": "<tenantKey>" }
に、name などの任意の表示用メタデータを加えたものです。ワークスペースシークレットで署名してください。
シークレットをブラウザーやクライアントのコードに公開しないでください。
Membrane トークンを取得する
membrane token [--tenantKey <key>] [--manager] [--expiresIn <duration>] [--json]
Membrane API へのリクエストを認証するための、有効期間の短い JWT である Membrane トークンを生成します。バックエンドコードからのリクエストで、Authorization: Bearer <token> ヘッダーに設定するトークンが必要な場合に、このツールを呼び出してください。フロントエンドコードでは、バックエンドに一時トークンをリクエストしてから、フロントエンド SDK を使用してください。
入力
tenantKey(任意)— トークンがアクセスする接続の分離されたスコープを識別します。デフォルトでは、現在のコンソールセッションに紐づくテナントが使用されます。manager(任意、CLI のみ)—POST /actions、インテグレーションパラメーターの編集、インポート/エクスポートなど、ワークスペースレベルの変更に使用するワークスペースマネージャートークンを発行します。エンジンのworkspaceAsManager認証レイヤーに対応します。managerが設定されている場合、テナントキーは任意です。(ペイロードには、エンジンの既存の認証解決との後方互換性のため、isAdmin: trueが含まれます。)expiresIn(任意、CLI のみ)— トークンの有効期間です。例:5m、1h、24h。デフォルト:1h。
レスポンス
JWT 文字列です。すべての API 呼び出しで、Authorization: Bearer <token> として渡してください。
トークンペイロード
| フィールド | 必須 | 説明 |
|---|---|---|
workspaceKey | はい | ワークスペースキーです。 |
tenantKey | いいえ | テナント(ユーザー、チーム、または組織)の識別子です。ワークスペースレベルの操作では省略します。 |
name | いいえ | 人が読めるテナント名です(トラブルシューティングに役立ちます)。 |
fields | いいえ | テナントに関する追加メタデータを保存します。 |
isAdmin | いいえ | ワークスペース管理トークン(tenantKey なし)の場合は true に設定します。 |
コード例
JWT ライブラリがあれば、どの言語でも 3〜5 行で記述できます。必ずサーバー側で署名してください。ワークスペースシークレットを持つ人は、どのテナントのトークンでも発行できます。
import jwt from 'jsonwebtoken'
const token = jwt.sign(
{
workspaceKey: '<WORKSPACE_KEY>',
tenantKey: '<TENANT_ID>',
name: '<TENANT_NAME>',
},
'<WORKSPACE_SECRET>',
{ expiresIn: 7200, algorithm: 'HS256' },
)
import datetime, jwt
token = jwt.encode(
{
"workspaceKey": "<WORKSPACE_KEY>",
"tenantKey": "<TENANT_ID>",
"name": "<TENANT_NAME>",
"exp": datetime.datetime.now() + datetime.timedelta(seconds=7200),
},
"<WORKSPACE_SECRET>",
algorithm="HS256",
)
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
claims := jwt.MapClaims{
"workspaceKey": "<WORKSPACE_KEY>",
"tenantKey": "<TENANT_ID>",
"name": "<TENANT_NAME>",
"exp": time.Now().Add(time.Hour * 2).Unix(),
}
token, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte("<WORKSPACE_SECRET>"))
require 'jwt'
payload = {
workspaceKey: '<WORKSPACE_KEY>',
tenantKey: '<TENANT_ID>',
name: '<TENANT_NAME>',
exp: Time.now.to_i + 7200,
}
token = JWT.encode(payload, '<WORKSPACE_SECRET>', 'HS256')
String token = Jwts.builder()
.claim("workspaceKey", "<WORKSPACE_KEY>")
.claim("tenantKey", "<TENANT_ID>")
.claim("name", "<TENANT_NAME>")
.setExpiration(Date.from(Instant.now().plusSeconds(7200)))
.signWith(
new SecretKeySpec("<WORKSPACE_SECRET>".getBytes(), "HmacSHA256"),
SignatureAlgorithm.HS256)
.compact();
$token = JWT::encode([
'workspaceKey' => '<WORKSPACE_KEY>',
'tenantKey' => '<TENANT_ID>',
'name' => '<TENANT_NAME>',
'exp' => time() + 7200,
], '<WORKSPACE_SECRET>', 'HS256');
# Shell: sign via Node for a one-off curl call
TOKEN=$(node -e "
const jwt = require('jsonwebtoken');
console.log(jwt.sign(
{ workspaceKey: '<WORKSPACE_KEY>', tenantKey: '<TENANT_ID>' },
'<WORKSPACE_SECRET>',
{ algorithm: 'HS256', expiresIn: 3600 }
));
")
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"
トークンを取得するその他の方法
- 公開鍵/秘密鍵による署名 — より厳格な鍵管理が必要な場合は、ワークスペース所有の秘密鍵を使って任意の非対称アルゴリズム(
ES256、RS256、PS256、…)で署名し、ワークスペース設定に公開鍵を登録します。上記の例をそのまま置き換えて使用できます。 - ワークスペース管理トークン — テナント管理、ワークスペース設定、インポート/エクスポートなど、複数のテナントにまたがる操作には、
isAdmin: trueを追加し(tenantKeyは省略して)ください。 - テストトークン — API を手早く試したい場合は、コンソール設定のアクセスページから、事前に生成されたトークンを取得してください。
- 長期間有効なトークン — 実行中に更新できないサーバー間ジョブなど、期限切れにならないトークンが必要な場合は、アクセスページのクライアントトークンセクションから作成してください。
- 開発者トークン — すべてのワークスペースで Membrane アカウントとして操作します。コンソール設定の API トークンから発行します。
詳細については、認証ドキュメントを参照してください。
接続を一覧表示する
membrane connection list [--integrationKey <key>] [--search "<text>"] [--limit <n>] [--cursor <cursor>]
現在のテナントのアクティブな接続を一覧表示します。ユーザーに再認証を求める前に、接続がすでに存在するかどうかを確認するために使用します。
フィルター
integrationKey/integrationId— 1 つのインテグレーションに絞り込みます。search— 名前/キーを対象にフリーテキスト検索します。limit、cursor— 標準的なページネーションです。
レスポンス
各レコードには、id、key、integrationId、接続名、接続ステータスなどの識別情報が含まれます。接続に対してアクションを実行するには、connectionId または connectionKey を act に渡します。
コード例
# all connections
curl -sG $API/connections -H "Authorization: Bearer $TOKEN"
# filter to one integration
curl -sG $API/connections -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'integrationKey=github'
接続を削除する
membrane connection delete <identifier> [--json]
ID またはキーを指定して接続をアーカイブします。接続は
list-connections に表示されなくなり、act でも使用できなくなります。これは
切断操作です。Membrane はデフォルトでソフト削除します。復元または完全削除するには、生の
API を使用してください。
入力
connectionId— 接続のデータベース ID。
使用する場面
- ユーザーの接続を取り消す場合(例:ユーザーがアプリの設定から HubSpot を切断する場合)。
- テナントの削除やアカウントの整理 — テナントに紐づく接続をすべて アーカイブします。
- 設定を誤った接続を、新しく作成した接続に置き換える場合。
コード例
# Shell
curl -s -X DELETE "$API/connections/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
await membrane.connection('<id>').archive()
インテグレーションを一覧表示する
membrane integration list [--search "<text>"] [--connectors 1] [--externalApps 1] [--limit <n>] [--cursor <cursor>]
ユーザーが接続できる対象を確認します。ワークスペースのインテグレーションに加えて、有効な場合はカタログの コネクターや外部アプリから生成された合成インテグレーション(ユーザーが接続すると完全な インテグレーションになる候補)も返します。
フィルター
search— 名前、キー、説明を対象とするフリーテキスト検索。カタログ全体を参照するには省略します。connectors— Membrane カタログ内のコネクターを基にした合成インテグレーションを含めます(1で有効、0で無効。 省略した場合はワークスペースのデフォルトが適用されます)。externalApps— まだインテグレーションになっていない外部アプリについても同様です。limit、cursor— 標準のページネーション。
レスポンス
各項目には、connect にそのまま渡せる識別子(id、key、該当する場合は connectorKey)、表示名、ロゴが含まれます。
ユーザーの意図に合致するものがない場合は、この手順を省略して
intent: "…" を指定した connect を呼び出してください。サーバーがコネクターを検索し、
必要に応じてエージェントを起動し、その場で作成します。
コード例
# free-text search
curl -sG $API/integrations -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'search=crm'
# include catalog candidates that could become integrations
curl -sG $API/integrations -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'connectors=1' --data-urlencode 'externalApps=1'
接続
membrane connect [--integrationKey <key>] [--integrationId <id>] [--connectorKey <key>] [--connectorId <id>] [--connectionId <id>] [--connectionKey <key>] [--intent "<text>"] [--name <name>] [--non-interactive]
外部アプリへの接続を作成するか、既存の接続に再接続します。id、state、およびユーザーの認証がまだ必要な場合はclientAction.uiUrlを含む接続情報を返します。ユーザーがOAuthフローを完了できるよう、そのURLを渡してください。
返されたidは、再接続、検索、今後のデバッグで再利用する接続IDです。
接続先の指定方法
integrationKey/integrationId— ワークスペース内の既知のインテグレーションです。connectorKey/connectorId— コネクターです(インテグレーションがまだ存在しない場合に便利です。サーバーは必要に応じてインテグレーションを作成します)。intent— ユーザーが接続したい対象の自由記述です。サーバーがコネクターやインテグレーションを検索して、一致するものを選択します。一致するものがなければ、エージェントを起動してコネクターを構築します。connectionId— 既存の接続に再接続します(同じOAuthフロー、同じレコードです)。
list-integrationsから取得した具体的な識別子は、いずれかの項目にそのまま指定してください。組み合わせは自由です。たとえば、integrationKeyとconnectionKeyを指定すると、安定したキーのもとに新しい接続を作成できます。
intentまたはカタログ候補から新しいものを生成する必要がある場合、最初の接続には通常のOAuthリダイレクトより時間がかかることがあります。Membraneがカタログを検索したり、インテグレーションを作成したり、コネクターを構築したりする間、短いBUILDINGフェーズが発生します。この処理に失敗した場合、接続の状態はCONFIGURATION_ERRORまたはSETUP_FAILEDになります。
接続キー
後で接続を識別するために(act { connectionKey, ... }、list-connections、再接続時に)独自のconnectionKeyを渡してください。省略した場合、サーバーが割り当てます。
再接続
connectionIdに既存の接続(認証エラーになった接続など)を指定してconnectを呼び出します。同じOAuthフロー、同じレコードが使われ、新しい接続は作成されません。
レスポンス
接続オブジェクト全体です。
{
"id": "...",
"key": "...",
"state": "BUILDING" | "CLIENT_ACTION_REQUIRED" | "READY" | "CONFIGURATION_ERROR" | "SETUP_FAILED",
"clientAction": { "type": "connect", "uiUrl": "..." }?,
...
}
インタラクティブCLIはstateがREADYになるまで処理を待機します。MCPでは、接続パネルにclientAction.uiUrlが表示され、ユーザーが完了すると接続状態が更新されます。
コードでの使用
接続にはユーザーの操作が必要です。OAuth/同意フローを実際のブラウザーで実行する必要があるため、接続処理はフロントエンドに配置し、Membrane SDKのUIヘルパーを使用します。HTTP呼び出しを独自に実装しないでください。membrane.ui.connect(...)はホストされた接続画面をiframeオーバーレイとして開き、OAuthの往復処理とエージェントが構築したコネクタのフローを処理し、完了した接続を返します(ユーザーがダイアログを閉じた場合はnull)。
import { MembraneClient } from '@membranehq/sdk'
// Mint the token on your backend with get-membrane-token and hand it
// to the browser — never embed workspace secrets in front-end code.
const membrane = new MembraneClient({ token })
// Connect by connector key (catalog-level; resolves in a fresh workspace)
const connection = await membrane.ui.connect({
connectorKey: 'github',
connectionKey: 'github-dev',
})
// Connect by integration key (only once you've confirmed the
// integration exists in the current workspace, e.g. via
// `membrane integration list --search github`)
const existing = await membrane.ui.connect({
integrationKey: 'github',
connectionKey: 'github-dev',
})
// Connect by intent (agent may build a connector if needed)
const built = await membrane.ui.connect({
intent: 'connect my GitHub account',
})
// Reconnect an existing connection (same record, fresh OAuth)
const reconnected = await membrane.ui.connect({
connectionId: existing.id,
})
if (!connection) {
// User closed the dialog before completing the flow
return
}
Reactでは、@membranehq/reactのuseMembrane()フックを使って同じクライアントを取得できます。useMembrane().ui.connect({...})を使用すれば、MembraneClientを手動で設定する必要はありません。
アクションの一覧表示
membrane action list [--connectionId <id>] [--connectionKey <key>] [--intent "<text>"] [--limit <n>]
ワークスペース内で再利用可能なアクションを検索します。act(実行)やcreate-action(新規保存)を使う前に、keyで実行できるアクションを見つけるために使用します。
フィルター
connectionId/connectionKey— 特定の接続で利用できるアクションに絞り込みます。intent— 自然言語での説明です。接続(IDまたはキー)と組み合わせると、意味的な一致度で結果をランク付けします。接続を指定しない場合、intentは無視されます。limit、cursor— 標準のページネーションです。
レスポンス
各項目にはid、key、name、description、integrationKey、layer、inputSchema、outputSchemaが含まれ、そのままactに渡すのに十分な情報が得られます。一致するものがない場合は、create-action(intent経由)に進んでアクションを構築してください。
--externalAppIdでフィルターした場合に返されるカタログアクションのkeyフィールドは空です。これらはact --id <hex>で呼び出してください。ワークスペース内のアクションではkeyが空でないため、どちらの方法でも呼び出せます。
コードでの使用
# semantic match on a specific connection
curl -sG $API/actions -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'connectionId=<id>' \
--data-urlencode 'intent=create a contact'
Act
membrane act [--id <id>] [--key <key>] [--integrationKey <key>] [--connectionKey <key>] [--connectionId <id>] [--api <json>] [--code <text>] [--input <json>] [--meta <json>]
アクションを実行します。POST /act を使用して、再利用可能なアクションまたはインライン仕様を1回の統一された呼び出しで実行します。
インラインの api および code の実行は、アプリの機能、スクリプト、同期ジョブにおける基本的な構成要素です。安定したキーや共有スキーマが必要な場合、または複数の場所から呼び出せる抽象化が必要な場合は、再利用可能なアクションを使用します。
ディスパッチフィールドは必ず1つだけ
id— 再利用可能なアクションのデータベース ID。key— 再利用可能なアクションのキー。同じキーが複数のインテグレーションに存在する場合は、integrationKey/integrationId/connectionId/connectionKeyと組み合わせます。
カタログ/公開アクション(list-actions --externalAppId … から取得)には id があり、key はありません。これらは id でディスパッチします。ワークスペース内のアクションでは、id と key は相互に置き換え可能です。
api— 解決されたコネクションの認証レイヤーとベース URL を通じて送信されるインライン HTTP リクエスト:{ method, path, body?, headers?, query? }。code— サンドボックスで実行されるインライン JavaScript:module.exports = ({ input, membrane, connection, integration }) => …。 その戻り値がアクションの出力になります。
api と code にはコネクションが必要です。connectionId/connectionKey/integrationId/integrationKey を指定するか、ワークスペースのデフォルトを使用します。
コネクションの解決
優先順位:connectionId → connectionKey → インテグレーションのデフォルト(integrationId または integrationKey から取得)→ ワークスペースのデフォルト。POST /actions/:selector/run と同じ順序です。
レスポンス
{ "output": ..., "actionRunId": "..." }
4xx の場合も、レスポンスには actionRunId が含まれます。アクション実行ログでデバッグする際に使用します。
コード内での使用
# reusable action by key
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"key":"create-issue","integrationKey":"github","input":{"title":"Hello"}}'
# inline HTTP through a connection
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"api":{"method":"GET","path":"/user/repos"},"connectionKey":"github-dev"}'
# inline JS
curl -X POST $API/act -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"code":"module.exports = ({ input }) => ({ echoed: input })","input":{"hi":1},"connectionKey":"github-dev"}'
アクションの作成
membrane action create [--intent "<text>"] [--key <key>] [--name <name>] [--type <type>] [--config <json>] [--inputSchema <json>] [--integrationId <id>] [--integrationKey <key>] [--connectionId <id>]
再利用可能なアクションをワークスペースに保存します。方法は2つあります。
意図による作成 — intent: "…" とインテグレーションまたはコネクションのコンテキストを渡します。Membrane Agent がベンダーの API を使用してアクションを構築し、検証して登録します。レスポンスでは、アクションの id が BUILDING 状態で返されます。list-actions を id でフィルタリングして、state が READY になるまでポーリングし、その後 act で実行します。
明示的な仕様による作成 — type(例:api-request-to-external-app、run-javascript、list-data-records、…)、config(タイプ固有のペイロード)、および任意の key/name/inputSchema を渡します。仕様の形式がすでにわかっている場合に使用します。たとえば、インラインの act 呼び出しでテスト済みのものを再利用したい場合に適しています。
保存先
connectionIdを設定 → コネクションレベルのアクションインスタンス(1つのコネクションに限定)。integrationId/integrationKeyを設定(コネクションなし) → インテグレーションレベルのアクション(そのインテグレーション上のすべてのコネクションで利用可能)。- どちらも指定しない → ユニバーサルアクション(その仕様に対応するすべてのインテグレーションで利用可能)。
レスポンス
{ "id": "...", "state": "BUILDING" | "READY" | "ERROR", ...standard action fields }
アクションが READY になったら、id または { integrationKey, key } を指定して act で実行します。
コード内での使用
# intent
curl -X POST $API/actions -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"intent":"open a pull request","integrationKey":"github"}'
# explicit spec
curl -X POST $API/actions -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"key":"list-users","type":"api-request-to-external-app","integrationKey":"github",
"config":{"request":{"method":"GET","path":"/users"}}}'
アクションの取得
membrane action get <id> [--wait] [--timeout <seconds>] [--json]
ID を指定して、単一の再利用可能なアクションを取得します。id、key、name、state、type、config、inputSchema、outputSchema、および所有する integrationId/connectionId を含む完全なレコードを返します。
入力
id— アクションのデータベース ID。インテグレーションレベルのアクションと、コネクションレベルの ActionInstances のどちらにも使用できます。
使用する場面
create-actionがstate: BUILDINGを返した後、state: READYになるまでこれを使用してポーリングします。id/keyでactを呼び出す前に、アクションが存在し、想定する仕様と一致していることを確認します。actの呼び出しでアクションの設定に関する問題と思われるエラーが返された場合、保存済みのconfig/inputSchemaを確認します。
コード内での使用
# Shell
curl -s "$API/actions/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
const action = await membrane.action('<id>').get()
アクションを更新
membrane action update <selector> [--connectionId <id>] [--integrationId <id>] --data <json> [--json]
ID またはキーを指定して、再利用可能なアクションを部分的に更新します。マージ方式のため、変更するフィールドのみ渡してください。レコード全体を置き換える場合は、raw API(PUT /actions/:selector)を使用します。
入力
id— アクションのデータベース ID(または、ID かキーを受け付ける CLI の<selector>を使用します)。data— マージするフィールド。よく使われるものはname、description、config、inputSchema、keyです。正確な形式はアクションのtypeによって異なります。
スコープのヒント(CLI のみ)
--connectionId/--integrationId— キーが接続レベルのインスタンスとインテグレーションレベルのアクションの両方に存在する場合に、対象を特定します。データベース ID を渡す場合は不要です。
使用する場面
- 作成したばかりの再利用可能なアクションを反復的に調整するとき。プロンプトを洗練したり、名前を変更したり、スキーマを調整したりできます。再作成する必要はありません。
- テスト済みのインライン
actを再利用可能なアクションにする場合は、通常create-actionを使用しますが、その後のconfigの調整にはこのツールを使用します。
コードでの使用例
# Shell — merge update
curl -s -X PATCH "$API/actions/<id>" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"Create HubSpot contact"}'
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
const updated = await membrane.action('<id>').patch({ name: 'Create HubSpot contact' })
アクションを削除
membrane action delete <selector> [--connectionId <id>] [--integrationId <id>] [--json]
ID またはキーを指定して、再利用可能なアクションをアーカイブします。アーカイブされたアクションは list-actions に表示されなくなり、act で呼び出すこともできなくなります。Membrane はデフォルトでソフト削除します。復元(POST /actions/:id/restore)または完全削除には、raw API を使用してください。
入力
id— アクションのデータベース ID(または、ID かキーを受け付ける CLI の<selector>)。
スコープのヒント(CLI のみ)
--connectionId/--integrationId— キーが接続レベルのインスタンスとインテグレーションレベルのアクションの両方に存在する場合に、対象を特定します。データベース ID を渡す場合は不要です。
使用する場面
- うまくいかなかった実験用アクションを整理するとき。
- 新しいキーに置き換えられた、古いインテグレーションレベルのアクションを削除するとき。
コードでの使用例
# Shell
curl -s -X DELETE "$API/actions/<id>" -H "Authorization: Bearer $TOKEN"
// SDK
import { MembraneClient } from '@membranehq/sdk'
const membrane = new MembraneClient({ token })
await membrane.action('<id>').archive()
アクション実行ログを取得
membrane action-run-log get <actionRunId> [--details]
単一のアクション実行に関する Membrane の記録を取得します。すべての act の応答には actionRunId が含まれます(成功時と 4xx エラー時の両方)。この ID をここに渡すと、実際に何が起きたかを確認できます。
入力
actionRunId—actが返す ID。details(任意)— true の場合、実行の詳細な内容も含まれます。外部アプリとの未加工の HTTP 通信、マッピングされた入力、出力が含まれます。
応答
{
"id": "...", "status": "success" | "error",
"action": { ... }, "connection": { ... }, "integration": { ... },
"input": ..., "output": ..., "error": ...,
"details"?: { "request": {...}, "response": {...}, ... }
}
概要フィールドから、呼び出しの内容を確認できます。アクションが失敗した場合は、details を取得すると、Membrane が行った正確な HTTP リクエストと、外部アプリから返された内容を確認できます。
コードでの使用例
# summary
curl -sG $API/action-run-logs/<runId> -H "Authorization: Bearer $TOKEN"
# raw HTTP exchange (request/response content)
curl -sG $API/action-run-logs/<runId>/details -H "Authorization: Bearer $TOKEN"