Skip to content

Interactive API

The Interactive API automates Epicor Prophet 21 as if a live user were operating the UI. Every business rule, validation, event, and lookup fires exactly as it would in the native PowerBuilder interface. This makes the Interactive API the correct choice whenever behavioral fidelity to the P21 application layer is required.

Base URL

https://{your-server}/uiserver0/ui/interactive/v1/

Session Required

All Interactive API calls require an active P21 session. Obtain a session token by calling POST /common/session via the Common Services API before using any endpoint below.


Conceptual Overview

The Interactive API works by instantiating real P21 application windows on the server. The server maintains full window state between requests — open forms, current row positions, pending changes, and unsaved data all persist on the server for the lifetime of the session. The client drives the window by sending the same events a user would: opening a window, changing a field, clicking a tab, pressing a button.

Because the server is running the actual P21 application code, every consequence of a UI action also occurs through the API:

  • Field-change events fire and may update other fields
  • Lookups populate dependent fields (e.g., entering a customer ID fills ship-to, terms, credit limit)
  • Validators run and return errors before a save is committed
  • Business rules attached to windows, tabs, and fields all execute
  • Required fields are enforced at save time

This is both the primary strength and primary performance cost of the Interactive API. For pure data throughput without business rule overhead, see the Entity API.


Operations Reference

Endpoint Method Description
/window POST Open a P21 window. Returns WindowId and initial window data.
/window GET Get the current structure and data of an open window (?id={windowId}).
/window DELETE Close an open P21 window and release server-side state.
/data GET Retrieve all data currently on the active surface of the window.
/data PUT Save the current window (equivalent to clicking the Save toolbar button).
/data DELETE Clear all data on the current window without saving.
/change PUT Send one or more field-value changes. Returns the new window state after all triggered events fire.
/row POST Insert a new blank row (equivalent to pressing Insert in a grid).
/row PUT Set the currently active row by index.
/row/data POST Insert a new row and immediately populate its fields in one round-trip.
/rows/limits PUT Set the active row range on a datawindow (for paging large datasets).
/rows/selected PUT Mark specific rows as selected.
/tab PUT Switch to a different tab on the window.
/tools GET List the toolbar tools available on the current window.
/tools POST Execute a toolbar tool (e.g., the Post button, Print button).
/values GET Retrieve the valid values list for a dropdown or code field.

POST /window — Open Window

Opens a P21 application window on the server. The window behaves exactly as if a user opened it from the P21 menu.

Request

Content-Type: application/xml

<WindowType xmlns="urn:epicor-com:p21:interactive:v1">
  <MenuId>OE-I-1</MenuId>
  <Name>Order Entry</Name>
  <ServiceName>oe_entry</ServiceName>
  <Title>Order Entry</Title>
</WindowType>
Field Type Required Description
MenuId string Yes The P21 menu identifier for the window (e.g., OE-I-1 for Order Entry). Find these in P21 under Setup → Menu Maintenance.
Name string No Descriptive name for the window instance.
ServiceName string No Internal service name for the window. Required for some windows that host multiple services.
Title string No Title to display (cosmetic only in API context).

Response

Returns an OpenWindowResult object:

<OpenWindowResult>
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <Status>Ready</Status>
  <Events>
    <Event>
      <Name>WindowOpened</Name>
      <Timestamp>2026-03-12T14:22:01Z</Timestamp>
    </Event>
  </Events>
  <Messages />
  <DataWindows>
    <DataWindow name="d_oe_hdr">...</DataWindow>
    <DataWindow name="d_oe_line">...</DataWindow>
  </DataWindows>
</OpenWindowResult>
Field Description
WindowId GUID that must be supplied on all subsequent calls for this window.
Status Current window state: Ready, Busy, Error.
Events List of events that fired during window initialization.
Messages Any P21 messages generated during open (warnings, info dialogs).
DataWindows Initial data loaded into the window's datawindows.

Save the WindowId

The WindowId GUID is required for every subsequent API call targeting this window. Treat it like a session token scoped to a single P21 form instance.


GET /window — Get Window State

Retrieves the full current state of an open window, including all datawindow data and window metadata.

Request

GET /window?id={windowId}
Parameter Type Description
id GUID The WindowId returned by POST /window.

Response

Returns the complete window structure including all datawindows, field definitions, row data, and current status.


DELETE /window — Close Window

Closes the specified window and releases all server-side state for it. Always close windows when done to avoid resource leaks on the P21 application server.

Request

DELETE /window?id={windowId}

Resource Management

Failing to close windows leaves orphaned window instances on the server. Each open window consumes server memory and a PowerBuilder context. Always close windows in your finally block.


PUT /change — Send Field Changes

This is the most frequently called endpoint. It sends one or more field-value changes to P21 and returns the updated window state after all triggered events, lookups, and business rules have fired.

Request

Content-Type: application/xml

<ChangeRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <Changes>
    <Change>
      <DataWindowName>d_oe_hdr</DataWindowName>
      <FieldName>customer_id</FieldName>
      <Value>C100001</Value>
      <Row>0</Row>
    </Change>
  </Changes>
</ChangeRequest>
Field Type Required Description
WindowId GUID Yes The window to send changes to.
DataWindowName string Yes PowerBuilder datawindow name (e.g., d_oe_hdr, d_oe_line).
FieldName string Yes Field name matching the P21 database column name in snake_case.
Value string Yes New value as a string. Dates: MM/dd/yyyy. Decimals: use period as decimal separator.
Row int No Zero-based row index within the datawindow. Defaults to the current active row.

Multiple Changes in One Request

You can batch multiple changes into a single ChangeRequest. They are processed in order. Each change's effects (lookups, events) complete before the next change is applied.

<ChangeRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <Changes>
    <Change>
      <DataWindowName>d_oe_hdr</DataWindowName>
      <FieldName>customer_id</FieldName>
      <Value>C100001</Value>
      <Row>0</Row>
    </Change>
    <Change>
      <DataWindowName>d_oe_hdr</DataWindowName>
      <FieldName>ship_via</FieldName>
      <Value>UPS GND</Value>
      <Row>0</Row>
    </Change>
  </Changes>
</ChangeRequest>

Response

Returns a ChangeResult containing the full updated window state:

<ChangeResult>
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <Status>Ready</Status>
  <ChangedFields>
    <Field datawindow="d_oe_hdr" name="customer_name">Acme Corp</Field>
    <Field datawindow="d_oe_hdr" name="credit_limit">50000.00</Field>
    <Field datawindow="d_oe_hdr" name="terms_code">NET30</Field>
    <Field datawindow="d_oe_hdr" name="ship_via">UPS GND</Field>
  </ChangedFields>
  <Events>
    <Event name="CustomerLookup" />
    <Event name="TermsDefaulted" />
  </Events>
  <Messages />
  <Errors />
</ChangeResult>

Cascade Effects

When you set customer_id, P21 populates customer_name, terms_code, credit_limit, ship_to_id, tax_code, and many other header fields automatically. The ChangedFields collection in the response reflects every field that changed — not just the field you set.


POST /row — Add Row

Inserts a new blank row into a datawindow, equivalent to pressing the Insert key in a P21 grid.

Request

<RowRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <DataWindowName>d_oe_line</DataWindowName>
</RowRequest>

Response

Returns the new row index and any default values P21 populated.


POST /row/data — Add Row With Data

Inserts a new row and immediately populates fields in a single call. More efficient than POST /row followed by PUT /change when you know all field values upfront.

Request

<RowDataRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <DataWindowName>d_oe_line</DataWindowName>
  <Fields>
    <Field name="item_id">WIDGET-001</Field>
    <Field name="unit_quantity">10</Field>
  </Fields>
</RowDataRequest>

Field Events Still Fire

Even when using /row/data, P21 still fires field-change events in sequence. Setting item_id will trigger the item lookup and populate pricing, unit of measure, and description before unit_quantity is applied.


PUT /data — Save

Saves all pending changes on the current window. Equivalent to clicking the Save (floppy disk) toolbar button.

Request

<SaveRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
</SaveRequest>

Response

On success, returns the saved record's key (e.g., the new order number). On validation failure, returns an Errors collection with the validation messages that prevented the save.

<SaveResult>
  <Status>Saved</Status>
  <KeyValue>SO-0045892</KeyValue>
  <Errors />
</SaveResult>

PUT /tab — Switch Tab

Switches the active tab on the window. Tab names are PowerBuilder tab control names (e.g., tab_line_items, tab_notes, tab_header).

Request

<TabRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <TabName>tab_line_items</TabName>
</TabRequest>

GET /tools and POST /tools — Toolbar Actions

GET /tools returns the list of available toolbar buttons for the current window. POST /tools executes one.

Execute a Tool

<ToolRequest xmlns="urn:epicor-com:p21:interactive:v1">
  <WindowId>3fa85f64-5717-4562-b3fc-2c963f66afa6</WindowId>
  <ToolName>post</ToolName>
</ToolRequest>

Common tool names include post, print, copy, delete, search. Actual available tools depend on the window.


GET /values — Valid Values

Returns the list of valid values for a dropdown or code-restricted field. Useful for building UI pickers or validating input before sending changes.

Request

GET /values?windowId={windowId}&datawindow={dwName}&field={fieldName}

Response

<Values>
  <Value code="UPS GND" description="UPS Ground" />
  <Value code="UPS 2DA" description="UPS 2nd Day Air" />
  <Value code="FEDEX" description="FedEx Priority Overnight" />
</Values>

Complete Workflow: Create a Sales Order

The following pseudocode illustrates a complete order entry session. Error handling is omitted for brevity.

# Step 1: Establish session (Common Services)
session = POST /common/session { username, password, company }

# Step 2: Open the Order Entry window
result = POST /window { MenuId: "OE-I-1" }
window_id = result.WindowId

# Step 3: Enter the customer — triggers customer lookup
#         P21 will populate: customer_name, terms, ship_via, tax_code,
#         ship_to addresses, credit_limit, and more.
POST /change {
  window_id,
  datawindow: "d_oe_hdr",
  field: "customer_id",
  value: "C100001"
}

# Step 4: Override the ship via if needed
POST /change {
  window_id,
  datawindow: "d_oe_hdr",
  field: "ship_via",
  value: "FEDEX"
}

# Step 5: Switch to the line items tab
PUT /tab { window_id, tab: "tab_line_items" }

# Step 6: Add a new order line
POST /row { window_id, datawindow: "d_oe_line" }

# Step 7: Enter item ID — triggers item lookup
#         P21 will populate: description, unit_of_measure, unit_price,
#         available_qty, lead_time, and more.
POST /change {
  window_id,
  datawindow: "d_oe_line",
  field: "item_id",
  value: "WIDGET-001"
}

# Step 8: Set quantity
POST /change {
  window_id,
  datawindow: "d_oe_line",
  field: "unit_quantity",
  value: "10"
}

# Step 9: Save the order
#         Returns the new order number (e.g., SO-0045892)
result = PUT /data { window_id }
order_no = result.KeyValue

# Step 10: Close the window and release server resources
DELETE /window { window_id }

Result

After step 9, SO-0045892 exists in P21 exactly as if an operator had manually entered it — complete with all business rules, allocations, pricing, and downstream effects applied.


Key Concepts and Gotchas

Window State Is Server-Side

The Interactive API is fundamentally stateful. The P21 application window lives on the server between API calls. This means:

  • You must DELETE /window when done, even on error paths
  • You cannot share a WindowId across threads
  • Session timeouts will invalidate open windows
  • High concurrency requires one window instance per concurrent operation

DataWindow Names

Field changes target a specific DataWindow — a PowerBuilder data control that maps to one or more database tables. Common DataWindow names:

Window DataWindow Covers
Order Entry d_oe_hdr Order header (oe_hdr table)
Order Entry d_oe_line Order lines (oe_line table)
Purchase Order d_po_hdr PO header
Purchase Order d_po_line PO lines
Customer d_customer Customer master
Inventory d_inv_mast Inventory master

Use P21's PowerBuilder source (if available) or the Transaction API service explorer to discover DataWindow names.

Field Names Are Database Column Names

Field names in the Interactive API are the actual P21 database column names, written in snake_case. They match the columns in the SQL Server tables exactly. When in doubt, check the sys.columns view in the P21 database.

Change Order Matters

When sending multiple changes, order is significant. If item_id must be set before unit_quantity (so the item lookup can validate the quantity against pack sizes), send them in that order — either as sequenced <Change> elements in a single request or as sequential calls.


Interactive API vs. Entity API

Scenario Interactive API Entity API
Business rules must fire Yes No
UI event chain required (lookups, defaults) Yes No
Speed / throughput is critical No Yes
Exact UI behavior required for compliance Yes No
Simple bulk data insert No Yes
Need to drive a P21 approval workflow Yes No
Reading reference data No (overkill) Yes
Integration that replaces a user action Yes No

Rule of Thumb

If a human user would do it through the P21 UI, use the Interactive API. If you are loading data that bypasses the UI (ETL, migrations, reporting), use the Entity API.


Error Handling

HTTP Status Meaning
200 OK Operation succeeded. Check Errors collection — a 200 can still contain field validation errors.
400 Bad Request Malformed request XML or missing required fields.
401 Unauthorized Session expired or invalid. Re-authenticate via Common Services.
404 Not Found WindowId not found — window may have timed out or been closed.
500 Internal Server Error P21 application error. Check the Messages collection for P21-level error detail.

Check the Errors Collection

HTTP 200 does not mean the P21 operation succeeded. A save that fails validation returns HTTP 200 with an <Errors> collection containing the blocking messages. Always inspect this collection.