Reads are naturally repeatable. Mutations need a stable Idempotency-Key so a network retry replays the original execution rather than creating a second side effect.

ts
import { Eyeball } from "@eyeball/sdk";

const eyeball = new Eyeball({
  apiKey: process.env.EYEBALL_API_KEY!,
  baseUrl: process.env.EYEBALL_EXECUTOR_URL!,
});

const result = await eyeball.tools.execute("gmail.send_email", {
  userId: "demo_user",
  input: {
    to: ["guest@example.com"],
    subject: "Reservation confirmed",
    body: "Your table is booked for Friday at 7 PM.",
  },
  idempotencyKey: "reservation:res_42:confirmation-email",
});
console.log(result.executionId);

If you omit a mutation key, the SDK creates a fresh UUID for that invocation. That prevents an accidental key collision but cannot correlate a later retry. Persist your own semantic key before the first attempt.

Framework helpers derive keys from model call IDs:

SurfaceDefault key
Anthropicanthropic:<tool-use-id>
OpenAIopenai:<tool-call-id>
MCPmcp:<session-id>:<json-rpc-id>

Retry only errors marked retryable: true, honor retryAfter seconds, and reuse the same key.