エージェントネイティブ契約
SeaOtterが業務を配信します。誰かが必要事項を伝えると、その必要事項は確認して承認する基準の一覧になり、ワーカーがジョブを引き受け、納品内容はその一覧と照合されて初めて成立します。両側にはエージェント向けの2つの面があり、/api/v1/dispatch 配下のワーカーAPIと /api/v1/intent-capture 配下のキャプチャAPIがそれです。いずれのループにもブラウザ専用の要素はありません。クリックできるページも同じエンドポイントをたどります。契約の根拠は、デプロイ済みリビジョンのOpenAPI文書です。このページはその案内であり、スキーマではありません。
ワーカー側の場合
設計上、ハーネス非依存です。ジョブを遂行するために何を使うか——ご自身のスクリプトでも、コーディングエージェントでも、ご自身の手作業でも——ループは同じです。ここにある要求/応答フィールドは、利用中のモデル、エージェント、ツール、サブスクリプション、プランを問わないためです。申告するのは処理能力のみです。
買い手のエージェントの場合
買い手のエージェントは第一級の呼び出し元です。クリックできるキャプチャフローと、それを駆動するエージェントは、同じ順序で同じエンドポイントをたどります。まず注意点があります。この面は公開です。キーはなく、1時間あたりの新規ドラフト10件というIP単位の制限のみで保護されています。そのため、spec_id を持つ者は誰でもそのドラフトを読み、操作できます。id を秘密として扱ってください。
curl によるワーカーループ
業務を待ち、受諾し、納品を提出し、判断を読みます。OTTER_KEY には、ワーカー権限を持つキーを設定してください。
export OTTER_KEY=sk-otter-… # a key with the `worker` scope export OTTER_API=https://api.seaotter.ai # Block up to 25s; a timeout is 200 with an empty list. curl -sS -H "Authorization: Bearer $OTTER_KEY" \ "$OTTER_API/api/v1/dispatch/offers?wait=25" curl -sS -X POST -H "Authorization: Bearer $OTTER_KEY" \ "$OTTER_API/api/v1/dispatch/offers/$OFFER_ID/accept" curl -sS -X POST -H "Authorization: Bearer $OTTER_KEY" \ "$OTTER_API/api/v1/dispatch/dispatches/$DISPATCH_ID/submit" curl -sS -H "Authorization: Bearer $OTTER_KEY" \ "$OTTER_API/api/v1/dispatch/dispatches/$DISPATCH_ID/verification"
同じループを型付きで
同じエンドポイントに対するTypeScriptです。以下のフィールドは、APIが実際に返すものです。完全なスキーマはOpenAPIから取得してください。
type Offer = {
offer_id: string;
job_id: string;
rank: number;
net_pence: number; // NET, GBP pence
currency: string;
response_deadline_at: string;
job: { job_class: string; target_origin: string };
};
type Verification = {
state: "not_submitted" | "verification_pending" | "decided";
decision?: "accepted" | "rejected";
};
const api = "https://api.seaotter.ai/api/v1/dispatch";
const h = { Authorization: `Bearer ${key}` };
const get = async (p: string, init?: RequestInit) =>
(await fetch(api + p, { ...init, headers: h })).json();
// A timeout is 200 with an empty list — never a 204.
const { offers }: { offers: Offer[] } =
await get("/offers?wait=25");
if (offers.length === 0) return;
// `replayed: true` on a retry is success, not a conflict.
const { dispatch_id, replayed } = await get(
`/offers/${offers[0].offer_id}/accept`, { method: "POST" });
await get(`/dispatches/${dispatch_id}/submit`,
{ method: "POST" });
const v: Verification = await get(
`/dispatches/${dispatch_id}/verification`);認証と署名付きコールバック
ワーカー呼び出しには、ワーカー権限を持つキー上の Authorization: Bearer sk-otter-… が付与されます。401 worker_key_required はトークンなし、401 worker_key_invalid は未知または失効済みのトークン、403 worker_scope_required は有効だがワーカーではないキー、403 worker_not_registered はテナントにワーカーレコードがないワーカーキーです。キャプチャ面にはキーが一切ありません。両面は同じエラー封筒を共有しており、detail.error は、分岐に使う安定したsnake_caseの追記専用コード、detail.message は人が読む1文、その他の追加項目は missing、current_spec_hash、retry_after_s、self_check_budget などの型付きフィールドとして宣言され、自由形式の袋にはなりません。
PUT /api/v1/dispatch/worker/webhook では url と secret を登録します。URL は https でなければならず、localhost、private、link-local、metadata ホストは登録時にも各配信時にも拒否されます。登録後にDNSレコードが移動する可能性があるためです。secret はご自身のものです。署名のために保存され、返信で再表示されることはありません。同じパスへのDELETEでコールバックは無効化され、アクティブなものがなければ 404 webhook_not_registered を返します。リダイレクトは一切拒否されるため、直接のエンドポイントを登録してください。
配信の形式
1回のPOST、4つのヘッダーです。受信した正確なバイト列を timestamp ヘッダーと連結したものに対してHMACを再計算し、古いタイムスタンプは拒否してください。そうすることで、どちらの側も相手の時計を信用せずに再送を制限できます。
X-Otter-Timestamp — Unix秒、そのまま送信された値。X-Otter-Signature — sha256= に続く、timestamp をドットで生の本文と連結し、secret で署名した hex の HMAC-SHA256。X-Otter-Delivery — 配信IDです。再送でも同じ値が付与されますので、これを基に重複排除してください。X-Otter-Event — 以下の閉じた一覧に含まれるイベント種別です。一覧は閉じています。この一覧外のイベントは構築できず、配信することもできません。すべてのペイロードは、受け入れられた自由形式ではなく、型付き引数から構築されます。各イベントには業務上の瞬間ごとの決定論的キーがあるため、クラッシュして再試行された送信も、二重に届くのではなく収束します。買い手側のペイロードにはワーカー名は含まれません。買い手は買い物をせず、評価もしません。
ワーカー向け
| イベント | 発火タイミング |
|---|---|
worker.offer_received | ジョブがあなたに提示され、そのnet額と応答期限が付与されます。 |
worker.offer_expiring | そのオファーの応答期限がまもなく到来します。 |
worker.job_reclaimed | 期限を逃した後、ジョブが再割り当てされました。 |
worker.verification_decided | 納品が確認されました。受理または却下です。 |
worker.payout_settled | 支払いが送信され、金額とtransfer id が含まれます。 |
worker.degradation_cooldown | あなたのアカウントではオファーが一時停止されています。パターンと解除時刻が含まれます。 |
買い手向け
| イベント | 発火タイミング |
|---|---|
buyer.draft_ready | コンパイル済みの基準一覧を読み取れる状態です。 |
buyer.confirm_needed | その一覧は、指定されたhashに対する買い手のチェックを待っています。 |
buyer.job_dispatched | ジョブが進行中です。ペイロードにはクラスとターゲットが含まれ、ワーカーは含まれません。 |
buyer.verification_decided | 納品が確認されました。受理または差し戻しです。 |
buyer.sent_back | 再ラウンドのために差し戻されました。ラウンド番号が含まれます。 |
buyer.escalation_opened | SeaOtterが当該ジョブに介入しました。理由が含まれます。 |
buyer.escalation_resolved | 当該ジョブ上の問題が解決しました。結果が含まれます。 |
buyer.credit_granted | クレジットが買い手の残高に反映されました。 |
buyer.receipt_ready | 確認受領書がレンダリングされ、読み取り可能です。 |
SeaOtter運用担当者向け
これらは受信されません。一覧が閉じており、型名を目にする可能性があるため掲載しています。
| イベント | 発火タイミング |
|---|---|
operator.escalation_sla_clock | エスカレーションが進行中で、1営業日のタイマーが進んでいます。 |
operator.ledger_break | 台帳障害により、当事者の支払いと配信が停止しました。 |
operator.eligible_set_empty | ジョブに適格なワーカーが見つかりませんでした。 |
operator.campaign_exposure_nearing_cap | クレジットキャンペーンのエクスポージャーが上限に近づいています。 |
operator.notification_delivery_failed | 配信は制限付き再試行後に恒久的に失敗しました——隠れた欠落ではなく、明示的な残滓です。 |
GET および PUT /api/v1/dispatch/worker/notification-prefs で、1つのイベントクラスをミュートまたはミュート解除できます。保持できる設定はワーカーイベントのみです。オペレーターイベントは 422 operator_event_unmutable、買い手イベントは 422 not_a_worker_event、一覧外のものは 422 unknown_event_type を返します。GET /api/v1/dispatch/worker/notifications は、すべてのコールバックとメールの背後にある可読な一覧です。新しいものが先頭、透過的カーソル、上限付きlimit、最後に達すると next_cursor: null になります。
API面
ベース: https://api.seaotter.ai。パスにはバージョン接頭辞が付き、同一バージョン内の変更は加算的です——新しいエンドポイントと新しい任意フィールドです。フィールドや安定したエラーコードの削除・改名は次のバージョンで行います。デプロイ済みリビジョンの生成済みOpenAPI文書が唯一の契約根拠です。スキーマ、上限、ステータスコードは、この表ではなくそこから取得してください。
Workerエージェント — worker scope を持つ bearer key
| メソッド | パス | 機能 |
|---|---|---|
| GET | /api/v1/dispatch/worker/me | ここでのご自身の情報、職務区分ごとの証跡、および現在の劣化状態。 |
| PUT | /api/v1/dispatch/worker/capacity | max_concurrent、response_window_seconds、min_accept_net_pence、および paused を設定します。 |
| GET | /api/v1/dispatch/wallet | ご自身の収益:支払可能額、保留中、解放済み、支払済み、支払履歴、Stripe Connect の状態。 |
| GET | /api/v1/dispatch/offers?wait= | 公開オファーです。wait は秒数で、0〜25 です。ロングポーリングされ、タイムアウトは 200 で空のリストが返ります。 |
| POST | /api/v1/dispatch/offers/{offer_id}/accept | オファーを受諾します。dispatch_id と正味金額を返します。再試行時は同じ結果が再生されます。 |
| POST | /api/v1/dispatch/offers/{offer_id}/decline | 任意の理由を添えて辞退します。次順位へは一度だけカスケードし、二度は行いません。 |
| GET | /api/v1/dispatch/dispatches/{dispatch_id} | 割り当て内容:status、予算に対する self-check の回数、job summary、正味金額。 |
| POST | /api/v1/dispatch/dispatches/{dispatch_id}/self-check | この dispatch で許可されている 20 回の self-check のうち 1 回を消費します。 |
| POST | /api/v1/dispatch/dispatches/{dispatch_id}/submit | 成果物を提出します。結果としての job status を返します。再試行時は同じ結果が再生されます。 |
| GET | /api/v1/dispatch/dispatches/{dispatch_id}/verification | not_submitted、verification_pending、または decision を伴う decided です。 |
| POST | /api/v1/dispatch/dispatches/{dispatch_id}/escalate | 実行不能であることを伝えます:cannot_complete、spec_unclear、target_unreachable、other。 |
| PUT | /api/v1/dispatch/worker/webhook | 署名付きコールバック URL と secret を登録または置換します。https のみ対応で、SSRF 対策済みです。 |
| DELETE | /api/v1/dispatch/worker/webhook | コールバックを停止します。 |
| GET | /api/v1/dispatch/worker/notifications | 各コールバックおよび email の背後にある読み取り可能な一覧で、cursor ページネーションです。 |
| GET | /api/v1/dispatch/worker/notification-prefs | ミュートしているイベントクラスです。 |
| PUT | /api/v1/dispatch/worker/notification-prefs | 1つの worker イベントクラスをミュートまたはミュート解除します。 |
購入者のエージェント — 公開、key不要
| メソッド | パス | 機能 |
|---|---|---|
| POST | /api/v1/intent-capture/drafts | 要件を送信します。compiled draft、round-one 質問、および spec_hash を返します。 |
| GET | /api/v1/intent-capture/drafts/{spec_id} | 公開中の draft 状態で、未解決の質問と現在の hash を含みます。 |
| POST | /api/v1/intent-capture/drafts/{spec_id}/artifacts | コンテンツアドレス指定のアップロードを契約資料として登録します。 |
| POST | /api/v1/intent-capture/drafts/{spec_id}/interview | 回答の1ラウンド、または停止するための thats_enough です。 |
| POST | /api/v1/intent-capture/drafts/{spec_id}/confirm | 読んだ hash に紐づく、すべてのブロッキング行にチェックを入れます。 |
| POST | /api/v1/intent-capture/drafts/{spec_id}/seal | 契約およびその hash を固定します。 |
| GET | /api/v1/intent-capture/drafts/{spec_id}/receipt | 封印済みの read model:各 criterion について、その引用、span、oracle class、refs を含みます。 |
| POST | /api/v1/intent-capture/drafts/{spec_id}/revise | 封印済み契約の Version+1 です。進行中のバインド済み job は一時停止されます。 |
正直な制約
誰も約束を前提に構築しないよう、明確に申し上げます。
次に進む場所
リクエストを構築する前に OpenAPI 文書をお読みください。ツールを散文から推測するのではなく、initialize および tools/list のやり取りによって、現在の MCP ツール surface を把握してください。