Integration guide

Wire Stvor in front of execution

Stvor integrates as a verification checkpoint before your payment rail, chain broadcast, or internal ledger write. Flow: commit → verify → settle — with a signed Trust Receipt inline from POST /verify for ALLOW and DENY.

@stvor/client on npmAPI live — https://api.stvor.xyz

Overview

Three HTTP endpoints on a single origin — flat paths, no /api/v1:

  • POST /commitments — anchor intent at approval time (payloadHash, not raw payload)
  • POST /verify — compare live payment hash → ALLOW or DENY + signed receipt inline
  • POST /receipt — optional settlement: attach txHash after ALLOW only
Note
There is no /agents/register endpoint. Do not install @stvor/sdk — that is an unrelated legacy library. Self-check with published test vectors before writing client code. Verify receipts in the browser verifier.

01 · SDK install

install.sh
npm install @stvor/client

Verification-only integrations can use @stvor/core for canonical hashing and offline receipt verification without the HTTP client.

quickstart.ts
import { Stvor } from "@stvor/client";

const stvor = new Stvor({
  baseUrl: "https://api.stvor.xyz",
  apiKey: process.env.STVOR_KEY!, // sandbox key on stvor.xyz/#try-now
});

const payment = {
  to: "0x4c1b82f71a9e3d0c8b5e6f2a1d4c7b9e0f3a8c1d",
  amount: "50000.00",
  currency: "USDC",
};

// 1. commit — posts payloadHash (fresh nonce each time)
const commitment = await stvor.commit(payment, {
  agentId: "agt_sandbox_demo",
  nonce: crypto.randomUUID(),
});

// 2. verify — signed receipt inline for ALLOW and DENY
const result = await stvor.verify(
  { from: "agt_sandbox_demo", ...payment },
  { commitmentId: commitment.commitmentId },
);

if (result.decision !== "ALLOW") {
  // result.receipt is a signed DENY — verify offline; do not settle
  return;
}

// 3. settle on your rail, then optionally attach txHash
await settleOnYourRail(payment);
// await stvor.settle(result.id!, txHash);

02 · Pilot onboarding (optional)

I stand up a Stvor checkpoint in front of your execution flow — commit intent, verify at execution, settle only on ALLOW — plus a signed Trust Receipt for every decision. I do the integration. At the end it works and you keep going, or you pay nothing further.

Week 1

Map your execution path

Identify where money actually moves: Stripe capture, on-chain send, internal transfer API. Stvor sits immediately before that call.
Week 1–2

Wire checkpoint + receipts

Commit at intent, verify at execution, settle only on ALLOW. Test with tampered payloads in staging. Every DENY gets a signed receipt too.
End

Handoff

Working checkpoint in your repo. You keep running it, or you pay nothing further beyond the pilot fee.
Message @kun_sapog

03 · Checkpoint placement

execution-flow.ts
// WRONG — verify after payment
await stripe.paymentIntents.capture(id);
await stvor.verify(...); // too late

// RIGHT — commit → verify → settle
const payment = { to: "0xVendor...", amount: "50.00", currency: "USDC" };
const commitment = await stvor.commit(payment, { agentId: "agt_01", nonce: crypto.randomUUID() });

const result = await stvor.verify(
  { from: "agt_01", ...payment },
  { commitmentId: commitment.commitmentId },
);

if (result.decision !== "ALLOW") {
  await stripe.paymentIntents.cancel(escrowId);
  // signed DENY in result.receipt
  throw new Error(result.reason);
}

await stripe.paymentIntents.capture(id);

04 · API: commit + verify

Commit at intent time — when the user confirms, when the agent proposes a tool call, or when a contract is created. POST /commitments takes payloadHash (SHA-256 of RFC 8785 payment payload), not raw payload. Store the commitmentId; do not mutate the committed hash.

Note
Public sandbox key (test env, rate-limited): stvor_test_tdesrpqe9auRopdcA2Ab-bFzF2xylw9v. Required header: Authorization: Bearer <key>. Use a fresh nonce per commitment — duplicates return 409.
commit.sh
# Payment payload (RFC 8785 — hash the canonical bytes, not this comment block):
# {"amount":"50000.00","currency":"USDC","to":"0x4c1b82f71a9e3d0c8b5e6f2a1d4c7b9e0f3a8c1d"}
# payloadHash = SHA-256(canonical) = b36c8892571afafab33f0f389c62293948a7ff61f3064eca12700582756cda78
# (agentId, nonce) must be unique per commitment — use a fresh nonce every time.

curl -X POST https://api.stvor.xyz/commitments \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer stvor_test_tdesrpqe9auRopdcA2Ab-bFzF2xylw9v' \
  -d '{
    "agentId": "agt_sandbox_demo",
    "payloadHash": "b36c8892571afafab33f0f389c62293948a7ff61f3064eca12700582756cda78",
    "alg": "sha256",
    "nonce": "<any-unique-string>",
    "expiresAt": "2026-12-31T23:59:59.000Z"
  }'
verify-allow.sh
# Same to / amount / currency as the committed payload → ALLOW (+ signed receipt inline)

curl -X POST https://api.stvor.xyz/verify \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer stvor_test_tdesrpqe9auRopdcA2Ab-bFzF2xylw9v' \
  -d '{
    "commitmentId": "cmt_FROM_COMMIT_RESPONSE",
    "intent": {
      "from": "agt_sandbox_demo",
      "to": "0x4c1b82f71a9e3d0c8b5e6f2a1d4c7b9e0f3a8c1d",
      "amount": "50000.00",
      "currency": "USDC"
    }
  }'
verify-deny.sh
# Swap to after commit → PAYLOAD_MISMATCH → signed DENY

curl -X POST https://api.stvor.xyz/verify \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer stvor_test_tdesrpqe9auRopdcA2Ab-bFzF2xylw9v' \
  -d '{
    "commitmentId": "cmt_FROM_COMMIT_RESPONSE",
    "intent": {
      "from": "agt_sandbox_demo",
      "to": "0x9d0000000000000000000000000000000000000001",
      "amount": "50000.00",
      "currency": "USDC"
    }
  }'
verify-gate.ts
async function gate(
  commitment: { payloadHash: string },
  liveParams: Record<string, unknown>,
) {
  const hashOk = timingSafeHashMatch(liveParams, commitment.payloadHash);
  if (!hashOk) return deny("PAYLOAD_MISMATCH");
  return allow();
}

05 · Trust Receipt

On ALLOW and DENY, POST /verify returns a signed ES256 (P-256) Trust Receipt inline in the response. Publish keys at /.well-known/public-key and /.well-known/stvor-keys.json so third parties verify offline. See ATS-1 spec.

After settlement on ALLOW, optionally call POST /receipt with txHash to attach on-chain proof. DENY paths do not settle.

06 · Payment rails

Stripe (reference — live)

Manual capture escrow: authorize at funding, capture only after verify returns ALLOW, cancel on DENY. This is the demonstrated reference implementation on nous.stvor.xyz.

Planned

x402 and OrbWallet are on the roadmap. Any HTTP payment API with hold/capture semantics can host the same checkpoint pattern — Stvor is rail-agnostic by design.