Prompt-ready

Every event arrives with a deterministic text rendering, ready to feed straight to a language model.

Every event carries a prompt_ready string — a short text rendering of what happened, rendered server-side, deterministic, and capped at 4 KB. Feed it directly to a model instead of hand-writing prompts for every provider your agent touches.

What it looks like

The same underlying transformation, applied per provider. Three examples:

Stripe — invoice.payment_succeeded

Raw webhook:

{
  "id": "evt_1NxYzabc",
  "type": "invoice.payment_succeeded",
  "data": {
    "object": {
      "id": "in_1PabcD",
      "customer": "cus_AbcDef",
      "amount_paid": 12000,
      "currency": "usd",
      "status": "paid"
    }
  }
}

event.prompt_ready:

Stripe invoice.payment_succeeded at 2026-04-19T12:34:56Z.
channel: billing-inbox (webhook)

Invoice in_1PabcD paid by customer cus_AbcDef.
Amount: $120.00 USD.
Status: paid.

GitHub — pull_request.opened

event.prompt_ready:

GitHub pull_request opened at 2026-04-19T09:15:22Z.
channel: nevo-prs (webhook)

Repository: nevo-sh/sdk-python
PR #42: "Add retry support to HTTP client" by alice.
Branch feat/retries → main.
Stats: +180 / -23 across 7 files.

Forwarded email

event.prompt_ready:

Email from alice@acme.com to support@in.nevo.sh.
Subject: Re: API limits.
Received 2026-04-19T08:22:04Z.

Body:
can we bump our monthly quota? we're hitting the 100k ceiling.

Why it exists

Without prompt-ready, code that feeds inbound events to a language model looks like:

if event.type == "webhook.received" and event.channel.type == "webhook":
    provider = detect_provider(event.webhook.headers)
    if provider == "stripe":
        payload = json.loads(event.webhook.body)
        if payload["type"] == "invoice.payment_succeeded":
            prompt = f"A Stripe invoice was paid: ${payload['data']['object']['amount_paid'] / 100:.2f} from customer {payload['data']['object']['customer']}…"
        elif payload["type"] == "customer.subscription.updated":
            prompt = f"…"
        ...
    elif provider == "github":
        ...

With prompt-ready:

prompt = event.prompt_ready

That’s the whole thing. The switch, the parser, the per-event string template — all gone.

Properties

  • Deterministic. Same event in, same text out. No model in the loop, no drift.
  • Bounded. Capped at 4 KB. Large payloads are summarised, not pasted verbatim; the full raw payload is always on event.data.
  • Versioned. prompt_ready_version on every event identifies which renderer generation produced the text — useful if you cache prompts or want to re-render old rows after a renderer improvement.
  • Per-provider. We ship first-party renderings for Stripe, GitHub, Linear, and email today. Anything else falls back to a generic webhook rendering that still reads cleanly.

When to render your own

When the default rendering misses something your agent cares about, ignore prompt_ready and build a prompt from event.data. The raw payload is untouched; nothing’s lost.

When the default rendering is wrong for everyone — let us know. Per-provider fixes ship fast.