Skip to main content
Backend scripts expose the same data functions available in the template engine, so you can query your brand data and use the results for logic, not just display. All data functions that hit the database are asynchronous under the hood, but they look synchronous in your code — just call them and use the return value directly. Pure-computation helpers (like formatPrice, productSavings) are truly synchronous.

Customer

getCustomer()

Returns the full current customer object from the session (same as the customer global).

getUser()

Returns a lightweight { name, email } object for the current session customer.

Translations

Backend scripts use the same resolved locale as page templates (req.locale after host resolution). That value comes from route/query/session/domain/brand/Accept-Language and is then snapped to a row in Advanced → Translations when the brand has languages configured. See Internationalization for precedence and template helpers (t(), | t, and related). Most translation helpers are async in the sandbox — use await.

getLocale()

Returns the active resolved locale code for this request (same as req.locale), e.g. 'en' or 'ro'.

getLocales()

Returns the list of locale codes configured for the brand (admin sort order). Empty array when the brand has no translation languages.

getDefaultLocale()

Returns the brand default language code, or the first language by sort order, or '' when none exist.

t(key, params?)

Async. Tries the brand dictionary first for key, then built-in locale JSON. Optional params is a plain object for {{ name }}-style interpolation in built-in strings — not a fallback text argument. This is the default choice in backend scripts for the same keys you use with t() or | t in templates.

getTranslation(key)

Async. Reads only the brand dictionary (no built-in fallback); returns '' if the key is missing. Prefer t() unless you explicitly need that behavior.

getTranslations()

Returns a plain object copy of every key → value for the active resolved language (empty object when the brand has no entries).

getResolvedLanguage()

Returns the snapped language code used for brand translations (e.g. 'ro'), or the raw locale hint when the brand has no translation languages configured.

Orders

getOrders(sortBy?, limit?)

Fetch the current customer’s orders from the database.
Each order object contains:

getOrder(code)

Fetch one order for the current customer. Same scope as getOrders. The code argument is required: public order ID (order_number), internal order_id, or conversion code. Returns null if not found.
See Showing order details for template examples.

getOrderFulfillment(code)

Returns an allowlisted fulfillment summary for a single order. Same code rules as getOrder. Use this for status labels and timestamps shown to customers. Returns null when the order exists but has no fulfillment record yet (no integration / not scheduled), or when the order is not found. Returned fields (only these; raw fulfillment_provider_response and fulfillment_order_data are never exposed):

getOrderFulfillments()

Takes no arguments. Returns all tracking-style rows for the current customer across their orders (up to the same order cap as getOrders with limit 1000, newest first). Each row includes order_number so you can link to an order detail page. Duplicate (order_number, tracking_number) pairs are removed. Returned fields per row:
These APIs return filtered data only. Full provider API responses and internal fulfillment payloads are not available in backend scripts.

Bonus Products

getBonusProducts()

Fetch the current customer’s bonus products — digital downloads and extras they received as part of a purchase. Returns a deduplicated list of bonus items from all the customer’s orders.
Each bonus object contains:
bonus_url is the public download URL exposed to backend scripts. Internally, it comes from the saved conversion line when available, and otherwise falls back to the current bonus product download URL from the catalog. Bonus products are identified by conversion line items where is_bonus = true. The function enriches each item with full product details from the catalog. If the same bonus was granted across multiple purchases, it appears only once. Scoped to the current session (email and/or session id): if the visitor is not identified, the list is empty.

getAllBonusProducts()

Returns all bonus-classified products in the brand catalog (classification: bonus). This is not limited to what the current customer earned; it does not check orders or conversions. Object shape matches getBonusProducts() (including title, bonus_url, etc.). For catalog-only rows, bonus_url is the product’s stored download URL.
For the same filter but the full product shape (price, currency, merchant_product_ids, the normalized product_files array, etc.), use getProducts({ classification: 'bonus' }) instead.

getPurchasedProducts(sortBy?, limit?)

Customer-scoped helper for member-area “My Library” pages. Returns every product the current customer has purchased, fully catalog-enriched, with same-order bonuses already attached under included_bonuses[] and a pre-rolled-up downloads[] array (each entry tagged with kind). This replaces the manual recipe of:
  • calling getSessionOrders + getAllProducts + getBonusProducts separately,
  • joining order lines back to the catalog by code,
  • guessing which bonuses go with which product by matching strings in titles.
Each purchased product contains:
Bonus attribution rule: every is_bonus line in an order is attached to every main line of that same order. This matches how funnels actually deliver bonuses (the bonuses come with the offer, not with one specific catalog item). It also avoids the silent-drop bug of older recipes that filtered bonuses by title keywords. Dedup rule: one entry per product (keyed by id, then code). When the same product appears in multiple orders, the order matching the sortBy direction wins (newest purchase for 'newest'). Bonuses come from the kept order.
Scoped to the current session: returns [] when the visitor is not identified.

Subscriptions

Customer-scoped recurring billing functions. All operations are restricted to subscriptions belonging to the current session customer (matched by email) — a script can never read or modify another customer’s subscription. Write functions return { ok: boolean, error?: string, ...rest } and never throw on validation failure. They enforce status transitions automatically (e.g. only active can be paused or skipped, only paused can be resumed, already-canceled cannot be canceled again). For full request/response shapes, content-gating examples, and ready-to-use page-based API examples, see Subscriptions (backend template engine).
Per-request cache: All read helpers (getSubscriptions, getSubscription with an object arg, hasSubscription, countSubscriptions) share a per-request cache. The database is queried only once per request — subsequent calls with different filters read from memory.

getSubscriptions(status?, limit?)

List the current customer’s subscriptions, newest first.
Each subscription object contains:

getSubscription(arg)

Fetch a single subscription. Accepts either a transaction ID string or a filter object. String form — fetch by original transaction ID (existing behavior):
Object form — fetch the first matching subscription from the per-request cache:

hasSubscription(filter?)

Returns true if the customer has at least one subscription matching the filter, false otherwise. All fields are optional.

countSubscriptions(filter?)

Returns the number of subscriptions matching the filter. Same filter shape as hasSubscription, excluding date.

cancelSubscription(transactionId, reason?)

Cancel an active or paused subscription. Fires the subscription_cancel event.

pauseSubscription(transactionId, resumeAt?)

Pause an active subscription. Optional resumeAt is an ISO date string for an automatic resume — invalid dates return { ok: false, error: 'Invalid resume_at date' }.

resumeSubscription(transactionId)

Resume a paused subscription. If the stored next_charge_at is in the past, it is automatically advanced based on the subscription’s frequency.

skipSubscription(transactionId)

Skip the next billing cycle on an active subscription. Returns the new next charge date on success.

changeSubscriptionFrequency(transactionId, interval, intervalCount)

Change the billing cadence on an active or paused subscription. interval must be one of day, week, month, year. intervalCount must be a positive integer.

Conversions

getConversions(email)

Look up conversions (purchases) by email address.
Returns an array of conversion records with fields like public_order_id, created_at, customer_email, product_name, total, currency_code, status.

Products

getProduct(code)

Fetch a single product by its product code.

getProducts(filters?)

Fetch products with optional filters.

getAllProducts()

Fetch all products for the brand (no filtering).

getProductByCode(merchantCode, productCode)

Look up a product by merchant gateway code and the merchant-specific product code.

Product files and download URLs

Catalog products can include attached downloads. getProduct, getAllProducts, getProductByCode, and getProducts (with or without filters) all return the same normalized shape, so you can rely on the same fields in every code path. Use these fields: Inside each product_files row:

Metadata fields

The metadata object on each product_files row only exposes the following fields:
Internal storage pointers (provider, storage_path, brand_file_id) used to be present on this object and are now intentionally hidden — they were only useful to the platform itself and should not be relied on by scripts or templates.
Example — pick the primary bonus URL with a safe fallback, then list all digital files with their type:
Example — list every bonus across a filtered product set:
For the same field names in backend page templates (GetProduct / GetProducts / GetAllProducts), see Products, categories & shipping (backend).

productDownloads(product)

Roll up everything a member-area UI normally needs to render “what can the customer actually download for this product” into a single flat array. Replaces the recurring extractFiles / “classify by extension” gymnastics — every entry already carries kind, so you can switch on it without parsing URLs. Source order, deduplicated by URL (first occurrence wins):
  1. digital_files (purpose='digital')
  2. bonus_files (purpose='bonus')
  3. anything left in product_files not already picked up
  4. legacy scalar digital_file / bonus_file columns — surfaced only when no file rows exist, so you never get duplicates of a row that’s already in product_files
Each entry: Example — render a member’s library entry without any classify-by-URL logic:
The same helper is available in backend page templates as ProductDownloads(product) — see Products, categories & shipping (backend).

productSavings(product)

Compute savings info from a product’s price and retail_price. Returns an object:
Returns { amount: 0, percent: 0, amountFormatted: '', percentFormatted: '' } when there is no discount.

subscriptionSummary(product)

Returns a human-readable subscription description string.
Returns an empty string for non-subscription products.

formatPrice(value, currency?)

Format a numeric price with the given currency code.

Courses

getCourse(courseId)

Fetch a course by ID, including modules and contents.

getCourseBySlug(slug)

Fetch a course by its URL slug.

getModuleBySlug(courseSlug, moduleSlug)

Fetch a single module with its contents.

getCourses()

Fetch all courses for the brand with lesson and module counts.

getCoursesByCategorySlug(categorySlug)

Filter courses by category slug.

getCategoriesWithCourses()

Get all course categories with their courses grouped together.

getCourseForCustomer(slug)

Fetch a single course with enrollment data for the current customer. Returns the full module/lesson tree plus completed_content_ids, progress_percent, is_assigned, and started.

getCoursesForCustomer()

Fetch all courses the current customer is enrolled in. Same as getCourses(true).
Each course includes module_count, lesson_count, progress_percent, completed_content_ids, and is_assigned.

getCoursesForCustomerByCategorySlug(categorySlug)

Fetch the current customer’s enrolled courses, filtered by category slug. Returns an empty array if the category has no enrolled courses.

getCategoriesWithCoursesForCustomer()

Get all course categories that contain at least one course the current customer is enrolled in, with the enrolled courses grouped per category. Useful for category-tab navigation on a member-facing courses page.

getCompletedLessonsCount(opts?)

Count completed lessons across all enrolled courses, or for a specific course.

getCourseProgress(opts?)

Get a structured progress summary with per-course and overall completed/total lesson counts.

Blog

getBlog(blogId?)

Fetch a blog by ID. If no ID is provided, returns the default blog for the current domain.

getBlogArticles(blogId, page?, perPage?)

Fetch published blog articles with pagination.
Each article includes a body field with the article content.

getBlogArticle(blogId, slug)

Fetch a single published article by blog ID and slug.

getBlogCategories(blogId)

Get category summaries for a blog’s published articles.

getRelatedArticles(blogId, articleId, limit?)

Fetch related articles for a given article.
These functions generate URLs for the payment flow. They are synchronous.

buy(productCode)

Generate a purchase link for a product.

upsell(productCode)

Generate an upsell link (one-click purchase for existing customers).

downsell(productCode)

Generate a downsell link (same mechanics as upsell).

decline(productCode)

Generate a decline link (skip the upsell offer and proceed).

upsellUpgrade(oldSku, newSku)

Generate a subscription upgrade link. Produces a JavaScript action URL that calls the server API to change the customer’s subscription product. See Subscription Upsells.

upsellCancel()

Generate a subscription cancellation link. Produces a JavaScript action URL that calls the server API to cancel the customer’s subscription.

Assets

asset(path)

Generate a full CDN URL for a brand asset.
Automatically appends a cache-busting parameter when ?nc, ?no_cache, or ?preview_key is present in the request.

Visitor Control

whitelistVisitor()

Whitelist the current visitor by setting an encrypted cookie. Whitelisted visitors bypass access restrictions.

blacklistVisitor()

Blacklist the current visitor by setting an encrypted cookie.

Session Items

These functions read and write custom session items that are compatible with the template engine’s setSessionItem / getSessionItem functions. Values are stored with a custom_ prefix and base64-encoded. Use these when you need to share data between backend scripts and template-engine expressions on the same page or across pages within the same session.

setSessionItem(key, value)

Store a custom value in the session. Keys are sanitized to [a-zA-Z0-9_]. Values over 64KB are silently dropped. Pass null to delete.

getSessionItem(key)

Retrieve a custom session item. Returns an empty string if not found.

clearSessionItem(key)

Remove a custom session item.
For raw session access (without the custom_ prefix and base64 encoding), use session.get(key) and session.set(key, value) from the Actions page.

CRM Data

Entries vs. Fields: An entry is a single record or document stored in the CRM (like a row in a database, representing e.g. a specific check-in for a customer). A field (fieldKey) is a specific piece of data that lives inside an entry (like a column, e.g. “current_weight”).
CRM entries are stored per brand. Every read/write is scoped by:
  • brand_id — from the session (always applied server-side)
  • slug — the CRM entity machine name from entity settings (e.g. weight-tracker), required on every call. This is not the same as referenceType (customer, order, …).
  • reference_type + reference_id — which record the entry is attached to. Defaults: referenceType'customer', referenceId → current session customer_id when omitted or null.
Pass a single options object only (positional arguments are not supported). Missing slug or other required keys throws TypeError. Unknown slug for the brand also throws. Invalid field values still return false / null / [] after sanitization where applicable. Slug aliases: crmEntitySlug, crm_entity_slug. Reference aliases: reference_type, reference_id. Field key: fieldKey or field_key. Write functions (setCrmField, setCrmFields, addCrmFieldValue, clearCrmEntries) accept an optional async parameter (default false). When true, the write runs in the background — faster but with eventual consistency. Read functions are always synchronous.

ensureCrmEntity({ slug, name?, fields?, ... })

Idempotently provisions a CRM entity (and optionally seeds its field schema). Safe to call on every page render — existing entities and fields are never overwritten, only missing keys are inserted. Use this at the top of a backend script in templates that ship to many brands so the CRM entity is auto-created on first use, without requiring an admin to pre-configure it. Field type values: text, textarea, email, tel, url, number, integer, decimal, currency, date, datetime, time, select, multiselect, checkbox, boolean, radio, json, rich_text, wysiwyg (rich text fields: values are HTML strings). For select/multiselect/radio pass options: ['a','b'] or [{ value: 'a', label: 'A' }]. Returns:
Example:
Limits: max 5 ensureCrmEntity calls per page execution. Exceeding the limit returns { id: null, created: false, error: 'rate_limited' }.

getCrmEntries({ slug, referenceType?, referenceId?, limit? })

Fetch CRM entries for one entity slug and one reference.
Each entry object contains:

getCrmField({ slug, fieldKey, referenceType?, referenceId? })

Reads one field from the first matching entry for that slug + reference.

getCrmFields({ slug, fieldKeys, referenceType?, referenceId? })

Reads multiple fields from the first matching entries for that slug + reference. Returns an object mapping fieldKey -> value.

setCrmField({ slug, fieldKey, value, force?, referenceType?, referenceId?, async? })

Updates an existing entry’s field or creates a new entry under that entity slug and reference.
Performance tip: When setting multiple fields in one script, pass force: true. Without it, each call searches all entries (up to 100) to find the document containing that key. With force, it fetches only the newest entry (limit 1) and merges the field into it — much faster for sequential writes:

setCrmFields({ slug, fields, force?, referenceType?, referenceId?, async? })

Sets multiple field values for the same CRM entity + reference in one read/write cycle. This is the preferred API when one script needs to write several fields at once.
Snake_case keys are also accepted inside fields:
Behavior matches setCrmField:
  • If an existing entry for the same reference already contains one of the requested keys, that entry is updated.
  • If the keys are new but the reference already has entries, the newest entry is reused.
  • A new CRM entry is created only when that reference has no entry yet.

addCrmFieldValue({ slug, fieldKey, value, referenceType?, referenceId?, async? })

Creates a new CRM entry with the given field value. Unlike setCrmField which upserts a single value per key, this always creates a new entry — enabling multiple values for the same key over time.

getCrmFieldValues({ slug, fieldKey, limit?, referenceType?, referenceId? })

Collects all values for a given field key across CRM entries. Returns an array of the raw values (string, number, object, etc.), newest first.

clearCrmEntries({ slug, referenceType?, referenceId?, async? })

Deletes all CRM entries for the given entity slug and reference. Returns { ok: boolean, deleted: number }.

moveCrmLead({ slug, pipelineId?, pipelineSlug?, stageId?, stageSlug?, referenceType?, referenceId?, allEntries?, async? })

Moves a CRM entry to a specific pipeline and stage. Either pipelineId or pipelineSlug is required; either stageId or stageSlug is required. Returns { ok, moved, totalMatched, pipeline_id, stage_id } on success, or { ok: false, moved: 0, error }.

moveCrmToStage({ slug, stageId?, stageSlug?, pipelineId?, pipelineSlug?, referenceType?, referenceId?, allEntries?, async? })

Moves a CRM entry to a specific stage. The pipeline is resolved from the stage automatically — only stageId or stageSlug is strictly required (pipeline id/slug can narrow the lookup if stage slugs are not unique across pipelines).

moveCrmToPipeline({ slug, pipelineId?, pipelineSlug?, stageId?, stageSlug?, referenceType?, referenceId?, allEntries?, async? })

Moves a CRM entry into a pipeline. The entry lands on the pipeline’s first stage unless a specific stage is provided.

moveCrmToEntity({ fromSlug, toSlug, fieldMap, referenceType?, referenceId?, pipelineId?, pipelineSlug?, stageId?, stageSlug?, removeFromOld?, async? })

Copies field values from one CRM entity to another for the same reference. Use this to promote a lead to a deal, or migrate data between entity types. Returns { ok, moved, removed_from_old } where moved is the number of fields copied.