Skip to content

Quick Start

Get up and running with NLT in under five minutes. This guide covers installation, basic rendering, send suppression, and error handling.


Installation

bash
pip install pyrx-nlt
Note

Requires Python 3.10 or higher. NLT has zero third-party dependencies -- it uses only the Python standard library.

For development (adds pytest, ruff, mypy):

bash
pip install pyrx-nlt[dev]

Basic Rendering

Create an NLTRenderer with your data, then call render() with subject and body templates:

python
from pyrx_nlt import NLTRenderer
 
renderer = NLTRenderer(
contact={
"first_name": "Jane",
"email": "[email protected]",
"properties": {
"Plan": "premium",
"Balance": 250.75,
},
},
trigger_event={
"event_name": "purchase",
"attributes": {
"Order ID": "ORD-4821",
"Amount": 99.99,
},
},
)
 
result = renderer.render(
subject_template='Hi `{the user\'s First Name, or "there"}`!',
body_template="""
<p>Your order `{the Order ID from the trigger event}` is confirmed.</p>
<p>Total: `{the Amount from the trigger event, as "currency"}`</p>
 
`{if the user's Plan is premium}`
<p>VIP shipping: <strong>Free</strong></p>
`{else}`
<p>Standard shipping applies.</p>
`{end if}`
""",
)
 
print(result.subject) # "Hi Jane!"
print(result.html) # Rendered HTML with values substituted
print(result.suppressed) # False

RenderResult

The render() method returns a RenderResult dataclass with four fields:

python
@dataclass
class RenderResult:
html: str | None # Rendered body HTML, None if suppressed
subject: str | None # Rendered subject line, None if suppressed
suppressed: bool = False # True if a required field was missing
suppressed_reason: str | None = None # Why the send was suppressed
unresolved_expressions: list[str] = [] # Expressions that could not be parsed
has_rendering_errors: bool = False # True if any expressions were unresolved

In your email pipeline, always check suppressed before sending:

python
result = renderer.render(subject_template=subject, body_template=body)
 
if result.suppressed:
log.warning("Email suppressed", reason=result.suppressed_reason)
else:
send_email(to=contact["email"], subject=result.subject, html=result.html)

render_text() for Single Templates

When you need to render just one template string (not a subject/body pair), use render_text():

python
text = renderer.render_text("Hello, `{the user's First Name}`!")
# "Hello, Jane!"

Returns None if a required expression suppresses the send:

python
text = renderer.render_text("`{the user's Policy Number, required}`")
# None (if Policy Number is missing)

Passing Event Data

NLT supports three data sources. You control what data the renderer can access through the constructor:

Contact Data

The contact dict holds user profile information. Flat keys are accessed in snake_case; keys under properties use original case first, then snake_case:

python
contact = {
"first_name": "Jane", # `{the user's First Name}`
"email": "[email protected]", # `{the user's Email}`
"properties": {
"Plan": "premium", # `{the user's Plan}`
"Tags": ["vip", "active"], # `{the user's Tags}`
"Address": {
"City": "NYC", # `{the user's Address.City}`
},
},
}

Trigger Event

The event that triggered the current flow. Must include event_name and attributes:

python
trigger_event = {
"event_name": "purchase",
"attributes": {
"Order ID": "ORD-4821", # `{the Order ID from the trigger event}`
"Amount": 99.99, # `{the Amount from the trigger event}`
},
}

Additional Events

Named events beyond the trigger. Keyed by event name, each with an attributes dict:

python
additional_events = {
"signup": {"attributes": {"Source": "referral"}},
"payment": {"attributes": {"Amount": 150.00}},
}
# Template: `{the Amount from the payment event}`
# Template: `{the Source from the signup event}`

Error Handling and Suppression

Smart Quote Handling

NLT automatically normalizes apostrophe-like characters before parsing. If a user pastes {the user\u2019s Name} from Google Docs (which uses smart quotes), or types {the user\u0060s Name} with a backtick, the tokenizer converts these to the standard ASCII apostrophe ' so the expression resolves correctly. Supported characters: \u2018 \u2019 \u201B (smart quotes), ` (backtick), \u00B4 (acute accent), \u02B9 \u02BC (modifier letters).

Graceful Degradation

NLT never throws on malformed templates. Unparseable expressions are left as literal text:

python
# Typo in expression -- missing "user's"
text = renderer.render_text("Hello, `{the First Name}`!")
# "Hello, `{the First Name}`!" (left as-is, no crash)

Send Suppression

The required modifier prevents sending an email with critical data missing:

python
result = renderer.render(
subject_template="Order `{the Order ID from the trigger event, required}`",
body_template="<p>Thank you for your order.</p>",
)
 
if result.suppressed:
print(f"Suppressed: {result.suppressed_reason}")
# "Required attribute 'Order ID' is missing"

If any required expression in either the subject or body resolves to null, the entire result is suppressed -- both html and subject are set to None.

Missing Data

When data is missing and not marked required, NLT returns an empty string by default. Use the or modifier to provide a fallback:

python
# Missing First Name with no fallback → empty string
renderer.render_text("`{the user's First Name}`")
# ""
 
# Missing First Name with fallback
renderer.render_text('`{the user\'s First Name, or "Customer"}`')
# "Customer"
Tip

Use required for transactional data that must be present (order IDs, policy numbers). Use or "fallback" for personalization where a default is acceptable (names, preferences).


Next Steps