Build a service
A Parley service is a set of capabilities plus the protocol machinery around them, which the library provides. You write what's specific to your domain: what can be read, and how an intent turns into concrete, undoable plans.
Capabilities
import { service, create, update, remove, send, charge, money, clarify, ParleyError, fix } from "parley-protocol";
const shop = service({
id: "shop.example", // the audience id: proofs are bound to it, so keep it stable
name: "Example Meals",
summary: "Chef-made meal delivery.",
trust: [PRINCIPAL_KEY], // principals whose grants you accept
// requireGrants: true, // also require grants for ASK and INTENT
// proposalTtl: 600, // seconds a proposal stays committable
});ask is read-only by definition. Agents retry it freely, and it must not change anything the principal could observe.
shop.ask("shop.search", {
summary: "Search the menu",
params: { "query?": "string", "tag?": "high-protein|spicy|vegetarian|vegan", "max_cal?": "int" },
run: ({ params }) => catalog.filter((m) => !params.tag || m.tags.includes(params.tag)),
});intent returns one plan, several (alternatives), or clarify(question, options). Nothing happens until the agent commits.
shop.intent("shop.order", {
summary: "Order meals for delivery",
params: { items: [{ sku: "string", qty: "int" }], deliver: "date" },
risk: "low",
plan: ({ params }) => ({
summary: `4 meals for ${params.deliver} — 53.95 USD`,
effects: [create("order/o1001", "2× Tofu Pad Thai, 2× Shawarma"), charge("card ••4242", "53.95 USD")],
cost: money(5395), // minor units
undoWindow: 7200,
data: { subtotal: 47.96, delivery: 5.99 }, // not hashed, may hold floats; must not describe effects
apply: (ctx) => { // on COMMIT, at most once
ctx.progress("authorizing card", 0.3); // streamed to the agent as EVENTs
return placeOrder();
},
revert: () => cancelOrder(), // present, so the proposal is undoable
}),
});The compact param schema
Parameter names map to type strings: string, int, number, bool, date, datetime, any, or an enum such as low|medium|high. Append [] for arrays and — description for a note to the model. A trailing ? makes a parameter optional. A nested object is a nested schema, and a one-element array of one is an array of objects. Params are validated automatically, with fixes such as renaming a near-miss key.
Errors that teach
Throw ParleyError(code, message, { fix: [...] }). A fix is a sentence plus an optional params patch that should make the request succeed:
throw new ParleyError("conflict", `${to} overlaps "Design review"`, {
fix: [fix("use free slot 2026-09-27T10:00:00Z", { to: "2026-09-27T10:00:00Z" })],
});Serve it
import { listen, serveHttp } from "parley-protocol/node";
import { fetchHandler } from "parley-protocol";
await listen(shop); // parley://127.0.0.1:7447 (pass tls options for parleys://)
await serveHttp(shop, { port: 8080 }); // POST /parley, GET /.well-known/parley
export default { fetch: fetchHandler(shop) } // Cloudflare Workers, Bun, DenoWhat the library does for you
Budgets and EXPAND handles, idempotent commits and replays, requester binding, grant and proof verification, spend reservation, consent requests, auto-commit, undo windows and Lens. See the Python page for the same in Python, and the spec for the wire contract.