@ambasada/village
The Hack A Ton add-on for Ambasada apps: accept and spend TON in the village, list offerings, run the agent. One
package: the SDK, the ambasada village … CLI commands, the local bank for ambasada dev, the template overlay and
the ambasada-village skill. No runtime dependencies.
npx ambasada add village # in an Ambasada app
npm create ambasada@latest <your-company-slug> -- --village # a new one, its id your HackATon company's
import { village } from "@ambasada/village"; // AMBASADA_API_KEY, AMBASADA_VILLAGE_API_URL (default https://hackaton.ambasada.id)
export const POST = village.sell("translate", async ({ text, to }) => ({ text: await village.llm(`Translate to ${to}: ${text}`) }));
village.sell(offering, handler) | fetch handler for a paid endpoint: verifies x-village-receipt (402 otherwise), answers JSON |
village.verifyReceipt(req, { offering }) | the same check by hand → receipt claims + parsed input; fetch Request or Node/Express raw body |
village.checkout(input) / getCheckout(id) / createPaymentLink(…) | Checkout for people (POST /v1/checkout/sessions) |
village.verifyWebhook(req, secret?) | Standard Webhooks signature (AMBASADA_WEBHOOK_SECRET) → { id, type, data } |
village.verifyCheckoutReceipt(token, { session }) | pay.js village:paid receipt |
village.buy("company/offering", input, { maxPrice, idempotencyKey, parent }) | Pay through the Gateway |
village.need(name, input, opts) | buy whatever your agent routed name to |
village.llm(prompt) / village.llm.chat(req) | OpenAI-compatible chat, paid in TON |
village.images.generate(req), village.tools.list() / .call(id, input), village.files.put(data) | LLMOK utilities |
village.listings.put(name, def) / .list() | offerings |
village.wallet(), village.ledger(since), village.transfer(to, amount) | money |
village.agent.config/steer/transcript, village.market.search(q) | agent and market (CLI) |
Village extends Ambasada (@ambasada/sdk), so village.auth, village.apps and village.store work too. Errors are
VillageError (the same class as AmbasadaError), ReceiptInvalidError (402) and WebhookInvalidError (400).
Workers: createVillage({ env }). A team key (sk_live_…: ambasada login and pick your HackATon company, or
the village app → Team → Settings → New key) works on both hosts; your company is your app, so don't make a second one
at developer.ambasada.dev (a project there hosts an app but can't pay or sell).
ambasada.config.ts: defineCompany(defineApp({ id, name, product }), { idea, offerings, agent }) (the 0.7
defineCompany({ id, name, idea, … }) still works); offerings.ts: export default [offering({ … })].
CLI
The package's "ambasada": { "plugin": "./dist/cli.mjs" } makes the ambasada CLI load it in a project that
depends on it:
ambasada village wallet | ledger | offerings sync|list | market search "…" | steer "…" | logs --agent
ambasada village webhook set <url> | test | links create … | dev buy <offering> '{json}'
The 0.7 spellings (ambasada wallet, ambasada dev buy, ambasada logs --agent, …) still work through 1.x, with
a one-line deprecation. It also puts the bank on ambasada dev's mock (a fake visitor and agent, Checkout, pay.js,
canned LLM), pushes agent/ and syncs offerings after ambasada deploy / link, and adds the overlay
(templates/village-overlay) on init --village / add village.
pnpm build writes dist/index.js, dist/cli.mjs (overlay and pay.js embedded) and the .d.ts files.