Skip to main content
After submitting an intent, you receive an operation ID. Use it to poll the Orchestrator until the intent reaches a terminal state.

Poll for status

const baseUrl = "https://v1.orchestrator.rhinestone.dev";
const apiKey = "YOUR_RHINESTONE_API_KEY";

async function getIntentStatus(operationId: string) {
  const res = await fetch(`${baseUrl}/intent-operation/${operationId}`, {
    headers: {
      "x-api-key": apiKey,
      Accept: "application/json",
    },
  });

  if (!res.ok) {
    const errorBody = await res.text().catch(() => "");
    throw new Error(
      `Request failed: ${res.status} ${res.statusText}${errorBody ? ` - ${errorBody}` : ""}`
    );
  }

  return res.json();
}

// Poll every 2 seconds until terminal
async function waitForCompletion(operationId: string) {
  const terminal = new Set(["COMPLETED", "FAILED", "EXPIRED"]);

  while (true) {
    const data = await getIntentStatus(operationId);
    console.log("Status:", data.status);

    if (terminal.has(data.status)) return data;
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }
}

Intent lifecycle

An intent moves through the following statuses:
StatusMeaning
PENDINGSubmitted and in progress
PRECONFIRMEDAccepted by a solver, waiting for onchain execution
CLAIMEDSource funds claimed by the solver, destination execution pending
FILLEDExecuted on the destination chain, source funds not yet claimed
COMPLETEDFully executed and settled onchain
FAILEDExecution failed
EXPIREDMissed the execution deadline
COMPLETED is the only successful terminal state. FAILED and EXPIRED are error states — see Error handling for how to respond to them.

SDK shorthand

If you’re using the Rhinestone SDK, sendTransaction handles polling internally and resolves when the intent completes:
const result = await rhinestone.sendTransaction({ ... });
// resolves only after COMPLETED
Use the manual polling approach above if you need visibility into intermediate states, or if you’re working directly with the REST API.