Search by name, SuiNS, address, digest, object ID, coin type or checkpoint.

API
Receipts

Receipts

Before an agent signs, tide dry-runs the exact bytes and returns a receipt it signs: what the transaction does, what Sui enforces, and whether it matches the agent's intent. After it runs, tide compares the chain with the receipt.

Make a receipt

Send the full unsigned TransactionData (the bytes the wallet signs) and, optionally, the intent. Every receipt gets a page at tide.ad/proof/<id> to show the human.

curl
curl -X POST "https://api.tide.ad/v1/tx/receipt" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "tx_bytes": "AAAC...",
  "intent": { "action": "swap", "max_spend": { "USDC": "5" }, "min_receive": { "SUI": "4.9" } }
}'

tx.build (and the MCP tool build_transaction) returns a receipt with the bytes it builds. In MCP the tools are receipt_for_transaction, verify_receipt and confirm_execution.

What a receipt holds

FieldMeaning
intentWhat the agent said it means to do, as sent.
tx.digestThe blake2b digest of the bytes: the digest Sui will give the transaction.
tx.bytes_base64The exact bytes, so anyone can run them again.
simulationThe checkpoint of the dry run and its effects: status, every balance change, objects created, changed, moved and deleted, events and gas.
guardsWhat Sui itself enforces when the bytes run, in plain words: the swap minimum, and under an Agent Pass its caps, pools, price floor and budget.
checkstide's checks of the intent: spend at most, receive at least, nothing else leaves the wallet, deadline, the pass's rules, the pre-sign check.
verdictpass when every check passes, otherwise fail with the reason.

Verify it yourself

The signature is ed25519 over the UTF-8 bytes of tide-receipt-v1, a newline, and the receipt as canonical JSON (object keys sorted at every level, no whitespace). The public key is at api.tide.ad/.well-known/tide-attest.json. This script checks the signature, checks that the digest is the digest of the bytes, and runs the same bytes on a public Sui full node. A dry run at the same state gives the same effects; a later one can differ where prices or balances have moved since.

TypeScript
import { createPublicKey, verify } from "node:crypto";
import { SuiGrpcClient } from "@mysten/sui/grpc";
import { TransactionDataBuilder } from "@mysten/sui/transactions";

const canonical = (v: unknown): string =>
  v === null || typeof v !== "object" ? JSON.stringify(v ?? null)
  : Array.isArray(v) ? "[" + v.map((x) => canonical(x ?? null)).join(",") + "]"
  : "{" + Object.keys(v).filter((k) => (v as any)[k] !== undefined).sort().map((k) => JSON.stringify(k) + ":" + canonical((v as any)[k])).join(",") + "}";

const BASE = "https://api.tide.ad";
const RECEIPT_ID = "YOUR_RECEIPT_ID";
const { receipt, signature } = await (await fetch(`${BASE}/v1/receipts/${RECEIPT_ID}`)).json();
const { keys } = await (await fetch(`${BASE}/.well-known/tide-attest.json`)).json();
const key = keys.find((k: any) => k.key_id === signature.key_id);

// 1. tide signed this receipt
const spki = Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), Buffer.from(key.public_key_base64, "base64")]);
const ok = verify(null, Buffer.from("tide-receipt-v1\n" + canonical(receipt)), createPublicKey({ key: spki, format: "der", type: "spki" }), Buffer.from(signature.signature_base64, "base64"));

// 2. the digest is the digest of the bytes in the receipt
const bytes = Buffer.from(receipt.tx.bytes_base64, "base64");
const digestOk = TransactionDataBuilder.getDigestFromBytes(bytes) === receipt.tx.digest;

// 3. any full node gives the same dry run
const sui = new SuiGrpcClient({ network: "mainnet", baseUrl: "https://fullnode.mainnet.sui.io:443" });
const { response } = await sui.transactionExecutionService.simulateTransaction({
  transaction: { bcs: { value: bytes } },
  readMask: { paths: ["transaction.effects.status", "transaction.balance_changes"] },
});
const changes = response.transaction?.balanceChanges?.map((b) => `${b.address} ${b.coinType} ${b.amount}`);
console.log({ signedByTide: ok, digestMatchesBytes: digestOk, success: response.transaction?.effects?.status?.success, changes });

After execution

GET /v1/tx/{digest}/receipt-check?receipt=<id> reads the executed transaction and compares status, balance changes, objects, events and gas with the receipt. The answer is match, differs with one plain sentence per difference, not_executed, or other_transaction when the signed bytes were not the receipt's.

Agent Pass

When the bytes act under an Agent Pass, the receipt names the pass and checks the action against its rules as they stand: active, signed by its agent, the rule for the pair, per-trade and daily limits, listed pools, price floor and budget left. GET /v1/agent-pass/report?owner=0x... is a plain account of an owner's passes and everything signed under them.

Methods

MethodWhat it doesCredits
tx.receiptPOST /v1/tx/receiptSigned receipt for unsigned bytes: simulated effects, Agent Pass policy check, intent checks and a verdict, signed by tide.10
receipts.getGET /v1/receipts/:idA signed receipt by id.1
receipts.verifyPOST /v1/receipts/verifyCheck a receipt's signature and that its digest is the digest of its bytes.1
tx.receiptCheckGET /v1/tx/:digest/receipt-checkAfter execution: compare the transaction's actual effects with its receipt.2
tx.receiptCheckPostPOST /v1/tx/:digest/receipt-checkAfter execution: compare actual effects with a receipt you send in the body.2
meta.attestKeyGET /.well-known/tide-attest.jsontide's attestation public key (ed25519) for receipts.Free
agentPass.reportGET /v1/agent-pass/reportAgent Pass report: an owner's (or agent's) passes, their rules in plain words, spend against caps and every action, for a human.5
agentPass.precheckPOST /v1/agent-pass/precheckWill this Agent Pass accept the action? Each rule checked before anything is signed.2