Procurement · Quickstart

From request to receipt.

Create a policy-bounded request, choose a qualified provider, fund work, verify the exact submission, and settle with an auditable receipt.

One package is enough

Terminal
pnpm add @telaro/sdk

Configure the network, hosted API, organization session, and tenant. The API rechecks organization membership for every request.

TypeScript
import { createTelaro } from "@telaro/sdk";

const telaro = createTelaro({
  network: "devnet",
  procurementApiBase: process.env.TELARO_PROCUREMENT_API_URL,
  auth: { token: organizationSession },
  organizationId: "org_acme",
});
LocalTELARO_PROCUREMENT_API_URL=http://localhost:8080
Devnetnetwork: "devnet"
Production targetnetwork: "mainnet-beta"

This example runs in a trusted server process. The product app keeps its bearer in an HTTP-only session and sends browser commands through a same-origin gateway. Keep each environment's API URL, access token, organization, mint, RPC, and signer configuration together. Do not mix a devnet mint with a mainnet request, and do not enable mainnet spend until its deployment is explicitly authorized and validated.

Turn an outcome into an editable draft

Gemini can structure the goal, deliverable, budget, deadline, and acceptance method. The result stays in your form until a person reviews it and explicitly creates the request.

TypeScript
const draft = await telaro.procurement.interpretRequest({
  prompt: "Analyze support themes and rank the top retention opportunities",
  currentDraft: {
    maximumSpendUsd: "25",
    deadline: "2026-08-12",
    dataClass: "internal",
    acceptanceMethod: "human",
  },
});

// Show draft.goal, draft.deliverableDescription, and any
// clarifyingQuestions in an editable review screen.

Create a bounded request

The request defines what can be purchased before a provider is selected or capital moves.

TypeScript
const request = await telaro.procurement.createRequest({
  workspaceId: "ws_research",
  costCenterId: "cc_market_intel",
  goal: "Summarize this market report",
  deliverable: {
    kind: "json",
    acceptanceMethod: "deterministic",
  },
  maximumSpend: {
    currency: "USDC",
    mint: devnetUsdcMint,
    decimals: 6,
    amountAtomic: "5000000",
    cluster: "devnet",
  },
  deadline: "2026-08-04T09:00:00.000Z",
  dataClass: "internal",
}, { idempotencyKey: crypto.randomUUID() });

Put only public-safe or encrypted references in work URIs. A confidential classification does not make raw on-chain data private.

Discover and compare candidates

TypeScript
const comparison = await telaro.procurement.discoverCandidates(
  request.id,
  { category: 4, limit: 3 },
  { idempotencyKey: crypto.randomUUID() },
);

const selected = comparison.candidates[0];
console.log(selected.provider, selected.price, selected.trust.score);

Discovery applies hard price, asset, and organization-policy filters first. Signed AgentCard evidence and stable exclusion reasons remain available for audit.

TypeScript
const advice = await telaro.procurement.recommendCandidates(request.id, {
  discoveryId: comparison.id,
});

console.log(advice.recommendedProvider);
console.log(advice.recommendations[0].summary);

Gemini only sees candidates already retained by hard policy filtering. The API rejects invented or out-of-set provider IDs, and the user can keep the deterministic order or choose another eligible provider.

Freeze terms, then approve

TypeScript
let execution = await telaro.procurement.prepareSacpExecution(
  request.id,
  {
    discoveryId: comparison.id,
    provider: selected.provider,
    offering: selected.offering!,
    budgetAccountId: "budget_market_intel_usdc",
    workUri: "https://tasks.acme.example/encrypted/report-42",
    submitWindowSeconds: 3600,
  },
  { idempotencyKey: crypto.randomUUID() },
);

if (execution.status === "approval_pending") {
  execution = await approverProcurement.decideExecutionApproval(
    execution.id,
    { decision: "approved", reason: "Within delegated budget." },
    { idempotencyKey: crypto.randomUUID() },
  );
}

Use a separate owner, admin, or approver session when policy requires approval. The approval is bound to the immutable mandate hash; changed terms require a new decision.

Reserve and execute

TypeScript
const funded = await telaro.procurement.executeSacp(execution.id, {
  idempotencyKey: crypto.randomUUID(),
});

Execution reserves budget transactionally and creates or funds the sACP job through the buyer signer boundary. A response timeout does not prove failure—inspect financialState before retrying.

Provider submits; buyer syncs

The provider process signs its own submission. It never receives the buyer's organization token or buyer signer.

TypeScript
import {
  IdempotentSacpSubmissionAdapter,
  Web3SacpProviderChainDriver,
} from "@telaro/sdk/procurement/server";

const submissions = new IdempotentSacpSubmissionAdapter(
  new Web3SacpProviderChainDriver({ connection, provider: providerSigner }),
);

await submissions.ensureSubmitted({
  jobId: funded.sacp!.jobId,
  provider: providerSigner.publicKey.toBase58(),
  submissionUri: "ipfs://bafy-provider-result",
});

Once the provider transaction is observable, the buyer control plane synchronizes it.

TypeScript
const submitted = await telaro.procurement.syncSacpSubmission(
  funded.id,
  { idempotencyKey: crypto.randomUUID() },
);

Verify the exact submission, then accept

TypeScript
const verified = await telaro.procurement.verifySubmission(
  submitted.id,
  {}, // deterministic result comes from the server verifier
  { idempotencyKey: crypto.randomUUID() },
);

const terminal = verified.verification?.outcome === "passed"
  ? await telaro.procurement.acceptSacp(verified.id, {
      idempotencyKey: crypto.randomUUID(),
    })
  : await telaro.procurement.disputeSacp(
      verified.id,
      {
        reason: "The frozen acceptance contract did not pass.",
        evidenceUri: "ipfs://bafy-encrypted-dispute-evidence",
      },
      { idempotencyKey: crypto.randomUUID() },
    );
MethodWho supplies the outcome?
deterministicConfigured server verifier only
humanAuthorized reviewer with evidence
gemini_assistedFail-closed until the server integration is configured
bonded_evaluatorBound evaluator workflow

Failed or inconclusive verification cannot call acceptance. Open a dispute instead; only the frozen reason hash is written on-chain. The bound evaluator settles it, then call syncSacpVerdict.

Read the receipt

TypeScript
const receipt = await telaro.procurement.getReceipt(terminal.runId);

console.log(receipt.settlement.state);        // "settled"
console.log(receipt.settlement.transactionSignatures);
console.log(receipt.verification?.evidence); // absent on deadline reclaim
console.log(receipt.auditRoot);

The receipt links the request, frozen policy decision, approvals, provider, authorized and settled amounts, verification evidence, settlement signatures, and audit root.