OREOASIS← Home
Quickstart · under 5 minutes

Your first receipt in five minutes

Install the SDK, record one action, and open your public verify URL. That's the whole loop — signed in your process, verifiable by anyone. Verification is free forever.

1 · Install

bun add @oreoasis/sdk    # or: npm i @oreoasis/sdk

2 · Get an API key

Create a free account — we email you a one-time sign-in link (no password). In your dashboard, name your organization and mint a key. The key is shown once, so copy it right away and set it as OREOASIS_API_KEY. The free tier is live; upgrading is a click when checkout opens.

3 · Record an action

The receipt is signed locally with your agent's key. Inputs and outputs are hashed on your machine — raw content never leaves it.

import {
  OreoasisClient, generateAgentKey, agentKeyFromSecret, exportAgentSecret,
} from "@oreoasis/sdk";
import { readFile, writeFile } from "node:fs/promises";

// An agent signing identity — PERSIST IT. A key minted on every boot is a NEW
// agent every restart (see "Your agent's key", below).
const agent = await loadOrCreateAgentKey("my-bot");

const client = new OreoasisClient({ apiKey: process.env.OREOASIS_API_KEY!, agent });

const receipt = await client.record({
  action: { type: "tool.call", tool: "search_flights", summary: "Booked SEA→LHR" },
  inputs: [{ name: "query", content: "SEA to LHR, 2 pax" }],   // hashed locally
  outputs: [{ name: "result", content: "PNR ABC123" }],
});

console.log(receipt.verifyUrl);
// → https://oreoasis.com/verify/receipt/rcpt_8xKQ2m

async function loadOrCreateAgentKey(name) {
  const path = process.env.AGENT_KEY_PATH ?? "./.agent-key";
  try {
    return await agentKeyFromSecret(name, await readFile(path, "utf8"));
  } catch {
    const agent = await generateAgentKey(name);
    await writeFile(path, exportAgentSecret(agent), { mode: 0o600 });
    return agent;
  }
}

Your agent's key — persist it

generateAgentKey() mints a new identity every call. Minting on every boot means a different agent after every restart. Your receipts stay verifiable either way — the public key travels inside the envelope, so nothing you already issued is affected — but the continuity is gone: nobody can say “these forty thousand receipts are all the same agent.” Persist the key; ephemeral is the exception, for a script or a test. Treat the file as a credential: anyone holding it can sign as your agent.

What goes in a receipt — and what must never

inputs and outputs are hashed on your machine: raw content never leaves it. meta is the exception. It is free-form and ships verbatim into the signed payload, onto the public verify page, and into a hash chain whose tip is published to a public transparency log.

Transparency logs cannot be erased — not by you, not by us. That permanence is the whole point of anchoring, and it applies to mistakes exactly as faithfully as to evidence. So: no names, no emails, no card or account numbers, no free text a customer wrote. Use meta for boring annotations — a workflow id, an attempt number. It is capped at 8 KiB; over that the ingest refuses the receipt outright, deliberately loudly, because the alternative is discovering it after it is anchored.

Two hashing notes worth knowing early: sha256("") is a valid digest but a meaningless one — it is identical for every empty input, so omit a ref rather than passing empty content. And for anything large (say over 10 MB) or already hashed upstream, pass a precomputed sha256 instead of content.

4 · Open your verify URL

Click it. The page re-checks the signature in your browser and shows the verdict — Oreoasis is never contacted. Send the link to a customer, an auditor, or a stranger; they can check it the same way.

It will say “anchoring pending” at first, and that's normal: free-tier receipts anchor in batches, typically within a few hours (paid plans anchor each receipt immediately). Your receipt is signed and chained the moment it's accepted — anchoring adds the public existence-proof on top of that.

The page shows three times, labelled: claimed by agent (from your signed payload — it proves what was signed, not when), received by Oreoasis, and anchored. Only the last two are times Oreoasis attests to. A timestamp more than 24 hours in the future is refused outright, so a broken clock fails loudly instead of quietly producing evidence nobody should trust.

One line, every action

Prefer a middleware? wrap records a receipt around any function — completed or failed — and passes the result straight through:

const search = client.wrap("tool.call", searchFlights, {
  summary: (q) => `search: ${q}`,
  inputs: (q) => [{ name: "query", content: q }],
});

await search("SEA→LHR");   // runs searchFlights AND records a signed receipt

Typed errors (OreoasisRateLimitError, OreoasisPlanLimitError, …) and the full API live in the docs. Full interface: specs/product-contract.md.