ServicesPact

Pact

Staked agreements resolved by verification - four shapes over one machinery. An oath stakes on your own commitment, a bounty pays the hunter who proves the work, a market runs pari-mutuel predictions over outcomes, and a pool collects premiums and pays out when a covered event is verified. Stakes are escrowed in real Treasury wallets, every resolution passes through a verifier (manual, LLM arbiter, or human approval), and settlement produces a hash-stamped receipt.

When to reach for it: habit commitments with real consequences, task bounties paid on verified proof, internal forecasting markets, item insurance in a game economy, staked SLAs between agents.

When not to: competitive price discovery for one item belongs to Auction; plain transfers with no outcome to verify belong to Treasury; an approval flow with no stake attached belongs to HITL.

Concepts

  • Stakes are real Treasury balance. A pact names a Treasury domain and currency_id, gets its own escrow wallet at creation, and moves money by multi-leg transfers. Everyone who stakes must already hold a wallet in that domain and currency - a missing wallet fails with 400 wallet_unresolved, so provision wallets first.
  • Four kinds, one lifecycle - open (-> claimed for bounties) -> resolved -> settled, with disputed and voided as branches.
  • Money moves at settlement, not resolution. Resolving records the verdict and opens a dispute window; payouts happen only when the window closes (swept by a background worker) or a dispute is dismissed.
  • The creator operates the pact. Only the creator or a platform admin may resolve, void, or judge disputes. With a manual verifier the creator's word is the verdict - pick an arbiter or hitl verifier when the creator should not be trusted unilaterally.
  • Self-exclusion: the creator may not claim their own bounty or stake their own market (403 self_stake_forbidden).
  • Disputes are bonded. During the post-resolution window anyone may file one dispute by escrowing a bond. Upheld: the resolution rewinds and the bond returns. Dismissed: the bond forfeits to the creator and the pact settles immediately.

Create a pact

snug pact create --kind oath   --domain game --currency cur_gold --title "Run 3x this week" --stake 100
snug pact create --kind bounty --domain game --currency cur_gold --title "Fix the flaky test" --stake 500
snug pact create --kind market --domain game --currency cur_gold --title "Ship by Friday?" \
  --outcome yes:Yes --outcome no:No --fee-bps 500
snug pact create --kind pool   --domain game --currency cur_gold --title "Item insurance" \
  --stake 200 --pool-payout 300

Oaths and bounties require a positive --stake (escrowed immediately); markets must not have one (400 invalid_stake - their pools are seeded by positions); a pool stake is an optional seed contribution. Over HTTP the same create is POST /api/v1/pacts; the enveloped response, captured live:

curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"oath","domain":"docs4-pact","currency_id":"cur_gold",
       "title":"docs4: no sugar for 30 days","stake_amount":250,
       "forfeit_destination":{"kind":"sink"}}' \
  http://localhost:4000/api/v1/pacts
{
  "status": 201,
  "msg": "Created",
  "data": {
    "pact_id": "pact_0d73134d7e614e0782c6ae10133d9cb6",
    "kind": "oath",
    "status": "open",
    "stake": {
      "currency_id": "cur_gold",
      "amount": 250,
      "escrow_wallet": "wal_efd7fecf54673398e18523f2dad7f051",
      "escrow_transfer_id": "txn_2bacca5a76cb4b4d999daba549a38d8f"
    },
    "created_at": "2026-08-28T14:17:17.625562Z"
  }
}

An oath's forfeit_destination routes a failed stake: sink burns it, counterparty:<id> pays a named beneficiary, pool returns it to the creator. An optional deadline blocks claims and positions once passed (409 deadline_passed) but does not resolve the pact by itself.

Bounties: claim

snug pact claim --pact-id pact_94e4...

Claims are exclusive and first-wins: the claimant gets status: claimed plus an expires_at (claim TTL, 48 h by default), and every later claimant gets 409 already_claimed. The background sweeper reopens a claim whose TTL lapses without resolution. Claiming a non-bounty is 400 invalid_pact.

Markets: stake on an outcome

snug pact stake --pact-id pact_5ddc... --outcome yes --amount 600

Each position escrows immediately and returns the live picture - after a 600 stake on yes and a 400 stake on no:

{ "implied_odds": 0.4, "total_pool": 1000, "position": { "outcome_id": "no", "amount": 400, "settled": false } }

implied_odds is the outcome's share of the total pool. An unknown outcome id is 400 unknown_outcome; per-position amounts are bounded by the configured min/max stake. At settlement, winners split the losing pool pro-rata after the creator's basis-point fee - verified live: with a 5% fee the 600 winner collected 980 and the creator 20 of the 1000 pool.

Pools: join

snug pact join --pact-id pact_a3da... --amount 300

Each contribution escrows into the reserve and appears in the pact's contributions. On a fulfilled resolution the beneficiary receives the parametric payout (capped at the reserve) and the remainder refunds contributors pro-rata - verified live: a 500 reserve with a 300 payout settled as 300 to the beneficiary plus refunds of 80 and 120.

Resolve, dispute, settle

snug pact resolve --pact-id pact_b95d... --fulfilled --evidence "proof attached"
snug pact resolve --pact-id pact_5ddc... --outcome yes      # markets name a winner
snug pact void    --pact-id pact_0d73... --reason "mis-set" # refund everyone

Resolution verdicts, the three verifier kinds, dispute bonds, rewinds, and settlement receipts have their own page: Resolution, disputes, and settlement.

Finding pacts

snug pact list --kind market --status open --domain game
snug pact list -q insurance                       # full-text over title/description
snug pact list --filter created_by:eq:alice --sort-by created_at --sort-order desc
snug pact positions                               # the caller's market positions

List responses are a pacts array plus standard pagination; q, filter, and sort_by follow the shared search grammar. positions shows each position's settled flag and final payout.

Limits and configuration

Stakes are bounded (1 to 1,000,000 by default), markets take 2-32 outcomes, creator fees are capped at 10%, the dispute bond is 100, and windows default to 24 h (dispute) and 48 h (claim) - all tunable, along with the settlement sweeper, via the PACT_* variables in the Pact CONFIG reference.

Reference

  • Pact API - every endpoint, callable
  • Related: Treasury for domains, currencies, and wallets, Arbiter for LLM-judged verification, HITL for human approval, Auction for price discovery

On this page