レシピ

失敗した支払いを調べる

読み取り専用のサポートツール。支払い ID を入力すると、失敗の原因が返ります。

支払い ID を受け取り、決済 API とステータスページをもとに「この支払いはなぜ失敗したのか」に答えるサポートツールを作ります。エージェントは読み取りしかできません。返金も再試行も、何かを変更することもできません。セットアップにかかる時間は約 15 分です。

このような実行は、たいてい 10 〜 15 秒で終わります。費用の大半はコンピュートで、1 秒あたり $0.0002 で課金されます。エージェントが取得するページは 1 件につき $0.001 かかります。モデルの利用料はそのモデル自体の価格で課金され、選んだモデルによって変わるため、最初の数回の実行で cost_micros を確認してください。予算の $0.50 は上限であり、実際の料金ではありません。

  1. 必要なものを用意する

    • クレジットのある Ephemeral アカウントと、API キーで作成する 2 つの API キー。実行を開始するバックエンド用のメンバーキーと、接続を 1 回だけセットアップするための管理者キーです。ロールとキーを参照してください。
    • 決済 API のベース URL (HTTPS で、インターネットから到達できるもの) と、その読み取り専用キー。たとえば、支払いの読み取りしかできない制限付きキーです。
    • 1 つのドメインで公開されているステータスページ。ここでは status.example.com とします。
    • 結果を受け取る、サーバー上の HTTPS エンドポイント。
    ターミナル
    export EPHEMERAL_URL=https://app.ephemer.al
    export EPHEMERAL_API_KEY=eak_…          # member key, for your backend
    export EPHEMERAL_ADMIN_KEY=eak_…        # admin key, for setup only
    export EPHEMERAL_CALLBACK_SECRET=$(openssl rand -hex 16)   # 32 characters; keep it on your server
  2. プレイブックを書いてアップロードする

    プレイブックには、チームの知識を書きます。どの呼び出しを行い、返ってきた結果をどう読むかです。短く具体的にまとめてください。エージェントは、タスクと一緒に読み取り専用のファイルとして受け取ります。

    playbook.md
    # Failed payment playbook
    
    You can only read. The payments connection allows GET, and web access is limited to status.example.com.
    
    1. GET /payments/{payment_id}. Note status, amount, currency, failure_code and failure_message.
    2. GET /payments/{payment_id}/attempts. Check whether earlier attempts failed for the same reason.
    3. If failure_code is processor_error or timeout, read https://status.example.com for incidents around that time.
    4. Decide the cause:
       - card_declined, insufficient_funds, expired_card: the customer's bank or card. Suggest a retry or another card.
       - processor_error, timeout: ours or the processor's. Say whether the status page shows an incident.
       - fraud_review: a risk rule held it. Do not suggest bypassing it.
    5. Never guess. If a call fails or a field is missing, say so.
    
    Answer in at most six lines: the cause, the evidence (field and value) and the next step for the support agent.
    Do not include card numbers or customer email addresses.
    curl $EPHEMERAL_URL/v1/files \
      -H "Authorization: Bearer $EPHEMERAL_API_KEY" \
      -F [email protected]

    ファイルは playbook.md という名前で保存されます。もう一度アップロードすると新しいバージョンが追加され、このファイル名を指定しているワークフローは、実行の開始時に最新のバージョンを使います。ファイルを参照してください。

  3. payments-api 接続を作成する

    接続は、API のアドレスとキーを Ephemeral の内部に保管します。エージェントは名前で呼び出すだけで、キーを見ることはありません。allowed_methods を ["GET"] に設定すると、それ以外のメソッドはすべて拒否されます。

    curl -X PUT $EPHEMERAL_URL/v1/connections/payments-api \
      -H "Authorization: Bearer $EPHEMERAL_ADMIN_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "type": "http",
        "url": "https://api.example.com/v1",
        "auth": { "type": "bearer", "token": "rk_live_…" },
        "allowed_methods": ["GET"],
        "instructions": "Look up a payment with GET /payments/{id} and its attempts with GET /payments/{id}/attempts. Amounts are in cents."
      }'

    ダッシュボードで設定する場合は、接続を開き、接続を追加から HTTP API を選びます。名前に payments-api、ベース URL を入力し、Bearer トークン を選んでキーを貼り付け、許可する HTTP メソッドでは GET だけを選択します。instructions は、どのエンドポイントがあるかをエージェントに伝えるものです。エージェントに表示されるため、機密情報は含めないでください。

    接続を読み戻してもトークンは表示されません。GET /v1/catalog/connections は、メンバーに対して接続の種類と許可するメソッドを一覧表示しますが、URL や認証情報は含まれません。

  4. ワークフローを保存する

    ワークフローは、タスク、プレイブック、接続、制限を 1 つの名前にまとめて保存するため、バックエンドは支払い ID を送るだけで済みます。エージェントからは、接続は payments として見えます。Web アクセスはステータスページに限定し、このタスクにコマンドは不要なのでシェルはオフにします。

    curl -X PUT $EPHEMERAL_URL/v1/workflows/investigate-payment \
      -H "Authorization: Bearer $EPHEMERAL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "prompt": "Find out why this payment failed.",
        "files": [{ "file": "playbook.md" }],
        "connections": { "payments": { "ref": "payments-api", "allowed_methods": ["GET"] } },
        "web": { "allow": ["status.example.com"] },
        "shell": false,
        "timeout_seconds": 120
      }'

    model は設定していないため、各実行は開始時点のデフォルトモデルを使います。別のモデルを使いたい場合は、モデルカタログから選んでください。支払い ID、予算、コールバックはワークフローではなく、各実行に属します。

  5. バックエンドから実行を開始する

    ワークフロー名、支払い ID、予算、コールバックを送ります。Idempotency-Key により、再試行しても安全です。同じキーを再送すると、2 つ目の実行は開始されず、既存の実行が 200 で返ります。支払い ID やチケット ID など、再現できるキーを使ってください。

    const paymentId = "pay_123";
    
    const response = await fetch(`${process.env.EPHEMERAL_URL}/v1/runs`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.EPHEMERAL_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `investigate-${paymentId}`,
      },
      body: JSON.stringify({
        workflow: "investigate-payment",
        input: { payment_id: paymentId },
        max_cost_micros: 500000,
        callback: {
          url: "https://example.com/hooks/ephemeral",
          secret: process.env.EPHEMERAL_CALLBACK_SECRET,
        },
      }),
    });
    const run = await response.json(); // { id: "run_…", status: "queued", … }

    レスポンスは 202 Accepted で、queued 状態の実行オブジェクトが返ります。実行は、ワーカーが空き次第開始されます。コールバック URL は HTTPS で、公開アドレスに解決される必要があり、シークレットは 24 ~ 1,024 文字でなければなりません。条件を満たさないとリクエストは 400 で失敗し、何も開始されません。ホスト型サービスでは、402 はクレジットが予算に足りないことを意味します。

  6. コールバックを検証する

    実行が最終ステータスに達すると、Ephemeral は実行オブジェクトを含む署名付きの POST を 1 回送信します。内容を信用する前に、ボディの生のバイト列に対して署名を確認してください。署名の対象は、X-Ephemeral-Timestamp ヘッダー、ピリオド、ボディをこの順に連結した文字列で、シークレットを使って HMAC-SHA256 で署名されます。 詳しい方式はコールバックを参照してください。

    Node.js、依存パッケージなし
    import crypto from "node:crypto";
    import http from "node:http";
    
    const handled = new Set(); // use your database in production
    
    http
      .createServer((req, res) => {
        const chunks = [];
        req.on("data", (chunk) => chunks.push(chunk));
        req.on("end", () => {
          const body = Buffer.concat(chunks);
          const timestamp = req.headers["x-ephemeral-timestamp"] ?? "";
          const signature = req.headers["x-ephemeral-signature"] ?? "";
          const expected =
            "sha256=" +
            crypto
              .createHmac("sha256", process.env.EPHEMERAL_CALLBACK_SECRET)
              .update(timestamp + ".")
              .update(body)
              .digest("hex");
    
          const valid =
            signature.length === expected.length &&
            crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
          const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
          if (!valid || !fresh) return res.writeHead(401).end();
    
          const run = JSON.parse(body);
          if (!handled.has(run.id)) {
            handled.add(run.id);
            // Attach run.output to the support ticket for run.id here.
          }
          res.writeHead(204).end(); // any 2xx counts as delivered
        });
      })
      .listen(3000); // serve this behind HTTPS at /hooks/ephemeral

    2xx 以外の応答は、間隔を延ばしながら再試行されるため、同じ実行が 2 回届くことがあります。ハンドラーが実行 ID を記憶しているのは、そのためです。

  7. 結果を読む

    コールバックのボディは実行オブジェクトです。例の支払いでは、次のようになります。

    JSON
    {
      "id": "run_118622d16f00105f7bba6d2281774705",
      "status": "succeeded",
      "output": "- **Cause:** The customer’s bank declined the payment for insufficient funds.\n- **Evidence:** `failure_code: insufficient_funds`; `failure_message: The card issuer declined the charge: insufficient funds.`\n- **Attempts:** Both attempts failed for the same reason.\n- **Next step:** Suggest the customer retry after resolving the balance issue or use another card.",
      "created_at": "2026-10-03T09:14:05.118Z",
      "started_at": "2026-10-03T09:14:05.402Z",
      "finished_at": "2026-10-03T09:14:16.447Z",
      "max_cost_micros": 500000,
      "cost_micros": 2618,
      "billing_pending": false,
      "zero_data_retention": false
    }

    output は、エージェントの回答をテキストにしたものです。cost_micros は費用全体を 100 万分の 1 ドル単位で表した値で、ここでは $0.002618 です。成功しなかった実行は、status が failed、 timed_out、cancelled のいずれかで、出力の代わりに error を持ちます。何も表示しないのではなく、その内容をサポート担当者に見せてください。billing_pending が true の場合、費用はまだ暫定です。少し待ってから GET /v1/runs/{id} でもう一度実行を取得してください。エージェントが行った各呼び出しを確認するには、ダッシュボードで実行を開くか、その イベントをストリーミングします。

安全である理由

リスク防ぐ仕組み
エージェントが支払いを変更または返金する。接続とワークフローの両方が GET のみを許可し、それ以外のメソッドは HTTP method not permitted で拒否されます。
決済 API のキーが漏えいする。キーは Ephemeral の内部にとどまります。microVM にはネットワークもキーもありません。ブローカーが各リクエストにキーを付与し、レスポンスから認証情報を取り除きます。
データが未知のサイトに送られる。Web アクセスは status.example.com のみを許可し、リダイレクトも同じリストと照合されます。Web アクセスを参照してください。
エージェントがコマンドを実行する。shell が false です。
実行の費用が想定を超える。予算の 500,000 マイクロ ($0.50) は開始時に確保され、超えることはありません。timeout_seconds により、120 秒で実行が終了します。
誰かが結果を偽造する。エンドポイントは、署名が検証できたリクエストだけを受け付けます。
読み取り専用キーには読み取り権限だけを与える
接続はエージェントが呼び出せる範囲を制限しますが、読み取りしかできないキーのほうが、より確実な保証になります。決済プロバイダーで、この接続専用の制限付きキーを作成してください。そうすれば、そのキーだけを単独で無効にできます。
プレイブックを調整する
原因がわかっている失敗をいくつか実行して、出力を確認します。誤っていた場合はプレイブックを修正して、もう一度アップロードしてください。次の実行から新しいファイルが使われます。保存済みのフィールドと実行ごとの上書きがどう組み合わされるかは、ワークフローを参照してください。
© 2026 Ephemeral制作: erni.works
esc

「」に一致する項目はありません。