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.
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¶
| 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¶
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.
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¶
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 /windowwhen done, even on error paths - You cannot share a
WindowIdacross 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.