# How to create and call an Epicor Function

> Source: https://netray.co/erp-help/epicor-functions-how-to-create
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

Open Function Studio from the main menu (Customization > Function Studio) or from inside a BPM designer's Call Context widget, define a Library and Function with typed inputs/outputs, write the logic in the C# widget, then compile and test with the built-in test harness before calling it from a BPM directive, a dashboard, or another function.

## Kind

advanced

## System

epicor

## Erp

Epicor Kinetic

## Module

Customization / Epicor Functions

## Query

how to create an epicor function

## Alternate Queries

- epicor functions
- what are epicor functions
- epicor kinetic functions tutorial
- epicor function studio vs bpm
- reusable c# logic epicor functions
- call epicor function from bpm directive

## Applies To

Epicor Kinetic 2021.1 and later (Function Studio replaced ad-hoc BPM C# for reusable logic); similar patterns exist in 10.2.700+.

## Steps

### Build a reusable Epicor Function

**Items:**

- Open Function Studio: Customization > Function Studio (or the wrench icon inside a BPM Custom Code widget's Call Context).
- Create a new Library (a logical grouping, e.g. "CustomerHold") if one does not exist, then a new Function inside it (e.g. "GetCreditHoldReason").
- Define Inputs on the Inputs tab: name, data type (string, int, DateTime, decimal, custom BO type). Mark required inputs.
- Define Outputs the same way on the Outputs tab; a function can return multiple named outputs.
- Switch to the Data Definitions / Widgets designer if you need to query the database (Invoke DB / Query widget) before dropping into code, or go straight to the C# Method widget for pure logic.
- In the C# widget, write logic against the inputs; use Db (the Epicor DbContext-style accessor) for reads, and set output variables at the end.
- Click Test at the top of Function Studio, supply sample input values, and run - the trace pane shows each widget's execution and any exception with line number.
- Save and compile. The function is now selectable from any BPM directive's "Invoke Epicor Function" widget, from another function, or from a dashboard's custom code.
- For reuse across companies, keep server-side logic in the function rather than client customizations so it applies uniformly to UI, BPM, and REST calls.

## Sections

### Why Functions replaced raw BPM C#

**Body:**

Before Function Studio, reusable logic in Epicor lived either as a Method Directive's Custom Code widget (copy-pasted across BPMs, hard to version) or as a compiled custom DLL that required a server deployment. Epicor Functions give you a middle ground: logic is written and compiled inside the application, versioned as part of the customization layer, and callable from anywhere - BPM, dashboards, UBAQs, and even external REST callers if you expose the function as an API.

A Function is really a mini business object: it has a signature (inputs/outputs), can hold multiple widgets (queries, invoke BO, condition, C# method), and gets compiled to an assembly the same way BPM directives do. That means the same debugging discipline applies - check the trace, check for null Db results, and watch execution context (some functions run in a different company/session context than you expect).

### Common function patterns

**Body:**

Validation functions: take a key field (part number, customer ID) and return a bool plus a message; call from a Pre-Processing directive's Invoke Function widget with a Condition widget checking the bool output, then Raise Exception if false.

Lookup/enrichment functions: query a related table (e.g. get the primary contact's email for a customer) and return it as an output for a BPM or dashboard to consume, avoiding repeated inline SQL in multiple places.

Calculation functions: centralize a formula (e.g. custom margin or lead-time calculation) so Kinetic screens, BAQ custom code, and BPMs all call the same function instead of drifting out of sync.

**Code:**

// Inside the C# widget of a function
var cust = Db.Customer.FirstOrDefault(c => c.Company == callContextClient.CurrentCompany && c.CustNum == custNum);
if (cust == null) { outputMessage = "Customer not found"; outputIsValid = false; }
else { outputIsValid = cust.CreditHold != true; outputMessage = cust.CreditHold == true ? "Customer on credit hold" : "OK"; }

### Calling a function from BPM and from another function

**Body:**

In a Method Directive, drag the Invoke Epicor Function widget into the designer, pick the Library and Function, and map each input to a context field (e.g. ttOrderHed.CustNum) using the field chooser. Map each output to a temporary variable you can then branch on with a Condition widget.

Functions can call other functions - useful for building small composable utilities (a date-math function called by three higher-level functions) - but watch for circular references, which Function Studio will reject at compile time.

Functions can also be exposed as callable REST endpoints (Function as a Service) so external systems or a portal can invoke server-side logic without going through a full BO method - handy for lightweight lookups.

## Pitfalls

Forgetting to mark an input as required leaves it nullable in code, causing NullReferenceException if a caller omits it - always null-check even required inputs defensively.

Running heavy queries inside a function called from a Pre-Processing directive on a high-volume transaction (e.g. every part master save) can slow the whole screen - profile with the trace before deploying broadly.

Functions compiled against one company's customization layer may not exist for other companies until published/deployed there - check Multi-Company deployment after creating a function in a multi-company environment.

Db context inside a function is scoped to the current session; do not assume you can read data outside the caller's company without an explicit company filter.

Overwriting an existing function's signature (adding a required input) breaks every existing caller - add new inputs as optional with defaults, or version the function under a new name.

Testing only in Function Studio's test harness and never through the actual BPM path can miss context differences (e.g. ttOrderHed row state) that only appear in the real transaction.

## Ai Angle

SyteRay-style agents built for Epicor can read a library's existing Functions and BPM directives before generating a new one, so a requested "add a credit check function" reuses your existing GetCreditHoldReason pattern instead of duplicating logic, and flags when a proposed function would create a circular call chain - something that is easy to miss by hand until Function Studio's compiler rejects it.

## Faq

### Can an Epicor Function replace a BAQ?

**Answer:** Not directly - a BAQ is for building result sets to display or export, while a Function is for callable logic with typed inputs/outputs. A function can internally use a Query widget similar to a BAQ, and a function can be exposed as a data source for a dashboard, but they solve different problems.

### How do I debug a function that fails only in production?

**Answer:** Use the trace log (System Monitor or the BPM/Function execution log if enabled) rather than the design-time test harness, since production failures are usually context-dependent (missing row state, different company data). Add explicit try/catch with a logged message in the function itself for hard-to-reproduce cases.

### Can I call a REST API from inside an Epicor Function?

**Answer:** Yes, using standard .NET HttpClient calls inside the C# widget, but be careful about timeouts blocking a synchronous BPM transaction - for slow external calls, prefer an async/queued pattern (e.g. a scheduled task or Kinetic's REST helper widgets) instead of calling out synchronously from a Pre-Processing directive.

### Do Functions get included in a package/solution export?

**Answer:** Yes - Functions are part of the customization layer and are exported/imported through the same Solution Workbench or package export used for BPMs and dashboards, so they migrate between environments (dev to test to production) consistently.

## Search terms

epicor function studio, create epicor function, epicor functions vs bpm, epicor reusable c# logic, epicor kinetic function as a service, epicor invoke function widget, epicor function library, epicor bpm call function

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
