Skip to content

Code step examples

These examples are deliberately complete enough to paste into a Code step and adapt.

Phone number normalization with libphonenumber-js

Section titled “Phone number normalization with libphonenumber-js”
import parsePhoneNumber from 'npm:libphonenumber-js'
const raw = $steps['fetch-leads'].items as Array<{ phone: string }>
const normalized = raw.map((row) => {
const parsed = parsePhoneNumber(row.phone, 'US')
return {
...row,
phoneE164: parsed?.format('E.164') ?? null,
phoneCountry: parsed?.country ?? null,
phoneValid: parsed?.isValid() ?? false,
}
})
return { items: normalized, count: normalized.length }

Notion’s official SDK plus the notion-to-md converter gives you a clean way to pull a Notion page into a workflow as Markdown — useful for sync jobs, AI document ingestion, or anything that needs Notion content as text. Both libraries are pure JS and only need network access, so they run cleanly in the sandbox.

import { Client, isFullPage } from 'npm:@notionhq/client@2'
import { NotionToMarkdown } from 'npm:notion-to-md@3'
const notion = new Client({ auth: $env.NOTION_TOKEN })
const n2m = new NotionToMarkdown({ notionClient: notion })
const pageId = initial.pageId as string
// Retrieve the page. `pages.retrieve` returns a union of the full response
// and a partial response — narrow it with the SDK's built-in type guard so
// `page.properties` is safely accessible below.
const page = await notion.pages.retrieve({ page_id: pageId })
if (!isFullPage(page)) {
throw new Error(
'Notion returned a partial page response. Make sure the integration has access to this page.',
)
}
// Find the title property by *type* — Notion databases let users rename the
// title column to anything (Title, Item, etc.), but exactly one property per
// page has type 'title'.
const titleProp = Object.values(page.properties).find((p) => p.type === 'title')
const title =
titleProp?.type === 'title'
? titleProp.title.map((t) => t.plain_text).join('') || 'Untitled'
: 'Untitled'
// Convert the page body to Markdown.
const blocks = await n2m.pageToMarkdown(pageId)
const markdown = n2m.toMarkdownString(blocks).parent ?? ''
return {
title,
markdown,
lastEditedTime: page.last_edited_time,
notionId: page.id,
}

A downstream step can pipe {{ fetch-notion-page.markdown }} into an LLM Call for summarization, write it to a data store, or post it to Slack as a digest. To sync a whole database instead of a single page, wrap this in a for-each loop over notion.databases.query results.

Conditional logic with multiple data sources

Section titled “Conditional logic with multiple data sources”
const customer = $steps['lookup-customer'].body as { tier: string; createdAt: string }
const orders = $steps['fetch-orders'].body as Array<{ amount: number; status: string }>
const lifetimeValue = orders
.filter((o) => o.status === 'paid')
.reduce((sum, o) => sum + o.amount, 0)
const customerAgeDays = (Date.now() - new Date(customer.createdAt).getTime()) / 86400000
let segment: string
if (customer.tier === 'enterprise') segment = 'enterprise'
else if (lifetimeValue > 10000) segment = 'whale'
else if (customerAgeDays < 30) segment = 'new'
else segment = 'standard'
return { segment, lifetimeValue, customerAgeDays }
const response = await fetch(`https://api.example.com/users/${initial.userId}`, {
headers: { Authorization: `Bearer ${$env.API_TOKEN}` },
})
if (response.status === 404) {
return {
found: false,
userId: initial.userId,
$warning: {
code: 'USER_NOT_FOUND',
message: `No user with ID ${initial.userId}`,
},
}
}
if (!response.ok) {
throw new Error(`API returned ${response.status}: ${await response.text()}`)
}
const user = await response.json()
return { found: true, user }
  • npm — packages you can import with the npm: prefix
  • JSR — TypeScript packages, including the @std collection
  • Error Handling — how $error and $warning fit into QuickFlo’s error model (retry, continueOnError, $errors)
  • Template Syntax → Variables — the same $env, $connections, $vars, $steps, initial bindings, but as Liquid templates for non-code steps