On this page

Changelog

Disclaimer: This is unofficial, community-created documentation for Epicor Prophet 21 APIs. It is not affiliated with, endorsed by, or supported by Epicor Software Corporation. All product names, trademarks, and registered trademarks are property of their respective owners. Use at your own risk.


All notable changes to this documentation project are listed below, grouped by date. This project uses Conventional Commits.


⚠ P21 Breaking-Change Alerts

Standing alerts for P21 platform version changes that break or silently corrupt API integrations — maintained in P21 Breaking Changes by Version. Check before any upgrade.


2026-09-14 — v1.25.0

Two findings surfaced while building an unrelated downstream service (a typed P21 gateway) hit real tenant behavior for the first time — an unverified guess this repo had already flagged turned out wrong, and a genuine API-design gap in the Item service had no documented workaround.

2026-09-12 — v1.24.0

Workstream C tier 1 of the full-coverage program: all eleven business-object REST families from the discovery sweep, each explored via the official SDK contracts before a single live call was made, all verified against 26.1.5950.0.

2026-09-12 — v1.23.0

Runnable demos for everything v1.22.0 verified, plus a repo-wide C# authentication bug the demo work exposed.

2026-09-12 — v1.22.0

Workstream B of the full-coverage program: exercise the five write paths doc 15 had only described. All five got a live run on 26.1.5950.0; four now have a verified success path, one has a verified refusal path and an open success case.

2026-09-12 — v1.21.0

Doc 15 gets what the repo's other API docs already have: runnable examples in both languages, a payload, and a Postman folder — and building them surfaced a real counting error in the doc itself.

2026-09-12 — v1.20.0

Five families the tenant has always hosted, now documented — starting with the hazard they share.

2026-09-12 — v1.19.0

The endpoint that lists the endpoints — and the probe that was answering the wrong question.

2026-09-11 — v1.18.0

Production moved to 26.1.5950.0. The registry is build-indexed, so it was re-run rather than assumed.

2026-09-11 — v1.17.0

2026-09-11 — v1.16.0

A $filter cannot compare two columns, and on string columns it does not tell you so.

2026-09-11 — v1.15.0

The salesrep on an order is not on the order, and two schema columns mean something other than what they are called.

2026-09-11 — v1.14.0

The OData service is two surfaces, not one, and the empty-bodied 404 that fact produces is easy to read as "not exposed".

2026-09-11 — v1.13.0

w_message dialogs are controllable, not just auto-answered — this documentation said otherwise in three places and in both response-window example programs.

2026-08-30 — v1.12.2

2026-08-30 — v1.12.1

Two corrections to how this repo describes itself, both prompted by questions the docs could not answer from their own text.

2026-08-30 — v1.12.0

A fourth window-driving surface, three findings verified on top of it, and a payload this repo has been shipping broken. Everything below was re-run live on 26.1.5940.0 before it was written down; patterns and first verification from Alex Westemeier.

2026-08-29 — v1.11.0

Three undocumented surfaces from @yeshayak, verified against a live 26.1.5930.1 tenant and re-verified here on 26.1.5940.0 before merge. Two of the findings turn out to be build-dependent, which is recorded rather than smoothed over.

2026-08-29 — v1.10.0

Four open issues closed. The v1.9.0 findings finally swept through the pages people land on, the definitions/ folder made to agree with itself, the cookbook's coverage claim made true, and a direct-ship date hazard documented on both the read and write sides.


2026-08-26 — v1.9.0

The 2026.1 registry re-run end to end against a newer middleware build. Two entries retire, one gets a sharper mechanism, one is new — and the empty 500 everyone learned to recognize now means something else.


2026-08-25 — v1.8.11

Two claims checked mechanically rather than believed, and the 401 from v1.8.10 given an entry under the error people will actually search for.


2026-08-25 — v1.8.10

Every C# example in the repo failed to authenticate, and the doc note that would have explained it said the opposite.

Verified on 26.1: token → router → OData → Transaction all green through P21Client.CreateAsync(), and dotnet run --project Recipes now reaches a recipe's payload with no environment variables exported.


2026-08-25 — v1.8.9

A grid everyone had written off as undeletable, and the field that deletes it.

(From issue #140, independently re-verified end to end on 26.1: definition, every write case, the primary guard, row ordering, the revive path, both read surfaces, and the code_p21 mapping. "Inactive" (705) is rejected by this grid — ValidValues is authoritative about the labels, not just the codes.)


2026-08-20 — v1.8.8

A controlled A/B turned the most common error in the API into something quite different from what it looks like.

Role-scoped DynaChange rules, Application Security and window permissions all surface as this one undifferentiated message. The practical consequences are the point: a payload that works in development can fail in production because the service account there has a different role — and it will look like a schema problem; Required: true in the definition tells you nothing, because disabled-ness is evaluated per session; and the fix is a P21 configuration change, not a payload change. Test with the account that will actually run the integration@mrwuss


2026-08-20 — v1.8.7

The direct-ship wizard, driven end-to-end here rather than taken on report.


2026-08-20 — v1.8.6

Closes the cross-check. The wizard limitation now has a worked escape hatch.

The wizard itself was not re-driven here: our API user has no Buyer ID and granting one is an operator decision. Everything reachable without that change was verified; the rest is credited and marked as such.


2026-08-20 — v1.8.5

The AP buy-side cycle, verified end-to-end on our own tenant — PO 998302 → receipt 5706613 → voucher 1775027, each step SQL-confirmed.


2026-08-20 — v1.8.4

Investigating the cross-check findings turned two reported claims into precise, verified rules — and answered a question left open in v1.8.0.

Recorded honestly: the blanket "a transaction can commit while reporting Failed" did not reproduce on ordinary Order writes. It is specific to services that cascade into other documents, and to Interactive wizards that commit at intermediate steps. That distinction is the useful part.


2026-08-20 — v1.8.3

Cross-check against Alex Westemeier's verified process work — 17 new commits since the July pass. This entry ships the one finding that breaks something published earlier today; the rest are filed as #130#133.

This shipped immediately rather than waiting because the update-order-lines recipe went out earlier today telling readers to key TABPAGE_1.order and sweep open orders — exactly the bulk pattern that hits RMAs. Adds an RMA Service reference entry, a gotcha in the recipe and in Order Service Gotchas, and an INDEX row for the error text. Detect with oe_hdr.rma_flag and route per order rather than discovering it as a failure — found by Alex Westemeier while reassigning takers at scale; verified and documented by @mrwuss


2026-08-20 — v1.8.2

Completes the 26.2 help sweep — every one of the 1,251 articles scored for API relevance, 17 candidates triaged, two findings worth keeping.


2026-08-20 — v1.8.1

Help-site re-scrape for 26.2 and one correction it produced.


2026-08-20 — v1.8.0

Findings from two authoritative sources we had never systematically mined: the Epicor SDK and API Reference hosted on our own middleware, and the P21 26.2 help site. Everything marked verified was tested against a 26.1 tenant; vendor-documented items that could not be exercised say so.


2026-08-20 — v1.7.0

Continuation of the live-testing session, plus a fold-in of every open repo issue (all maintainer-verified production findings). Everything marked verified below carries a DB or read-back confirmation.


2026-08-20 — v1.6.1

Follow-the-thread session: each v1.6.0 fact was pushed one step further against the live 26.1 tenant, and every result below is DB-confirmed.


2026-08-19 — v1.6.0

Documented from a recorded community conference session on intermediate P21 API development (Felipe Maurer, 2026), then live-tested against a 26.1 tenant. The session covered ground this repo had used correctly in specific recipes without ever explaining generally — most importantly Keys. The testing confirmed the session's central claims exactly, corrected two, and settled two the session left open. Claims that were not re-tested say so inline and give the reason.

Verified live (26.1, 2026-08-19) — each step a real Order write with a /transaction/get read-back: two rows of one item at 5 and 10 with Keys: [] collapse to one line at quantity 10 (Succeeded: 1, no messages); Keys: ["unit_quantity"] splits them into two; two different items need no Keys at all; an update keyed on unit_quantity appends a third line instead of editing the second; the same update keyed on user_line_no edits in place. Corrected: "surplus keys are accepted silently" holds only for a key that is real and sent — a key naming a column absent from that row's Edits fails the transaction with General Exception: Sequence contains no matching element, and one naming a nonexistent column fails with Invalid column name: {name}. Both are hard failures, so only a real, sent, non-discriminating key is silent — community session, verified and documented by @mrwuss - feat: New Reading One Record — POST /transaction/get — the endpoint appears in a dozen examples in doc 03 but had no section of its own. It is a POST despite the name, it returns one record as a complete TransactionSet across every tab, and because the response is shaped like a request it doubles as a clone template: read a record, change the key, post it back (a configured user copied to a new hire, a customer modeled on an existing one). Adds the /transaction/get-vs-OData table — one record whole and pre-joined versus many records one table at a time — and the clone caveats: disabled/auto-generated fields come back in the response and IgnoreDisabled is not a reliable way to write them, popups and stale references stop the replay, read the clone back. Verified live (26.1, 2026-08-19): one Order read returns 102 elements across every tab, and multi-record retrieval worksN TransactionStates return N Transactions (new subsection Reading several records in one call), at a few hundred KB of JSON per record. Both clone caveats reproduced on a verbatim replay: cross-field validation rejected it (The Expedite Date must be on or before the Required Date), then disabled display columns did (Column is disabled: customer_name, then company_id). Filtering the read-back through the basics field list cloned it cleanly in one pass — community session, verified and documented by @mrwuss - feat: The interleaving rule stated generallyThe general rule: repeat the element pair, don't batch it. The API replays a payload as an operator would work the window, so a child element attaches to whichever parent row is current at that point in the sequence: item A → detail A → item B → detail B, never A → B → detail A → detail B, which silently applies both details to the last row. An element may repeat as many times as needed. Doc 03 previously stated this only as one-off sequences for lot items and contract break lines; those are instances of this rule. Verified live (26.1, 2026-08-19) on a two-line order with TP_EXTDINFO.extd_info: the batched form returns Succeeded: 1 with no messages and puts both descriptions on line 2, last one winning, leaving line 1's extended_desc null — community session, verified and documented by @mrwuss - feat: GET /api/v2/basics/{name} added to the endpoints table, with a note on which of the three discovery endpoints answers which question: definition for the full schema and dropdown valid values (this is where site-specific values such as carrier_id come from), defaults for defaults and a fillable template, basics for an abbreviated field list — carrying the session's warning that basics can omit fields you need and include fields you cannot write.

Verified live (26.1, 2026-08-19) — the endpoint is real and both halves of the warning hold. basics returns a Status: "New" TransactionSet skeleton with Keys prefilled from each element's KeyFields and IgnoreIfEmpty: true on every edit, so you fill in values and POST it. On Order all three endpoints return the same 102 elements, but basics carries 103 fields against definition/defaults' 1,266. Its header list omits customer_id, source_loc_id and ship_to_id — all needed to create an order, and omitting ship_to_id fails the save with the unattributed This column is required. — while including company_id, which is refused with Column is disabledcommunity session, verified and documented by @mrwuss - feat: Lowercase item_id creates an item the UI cannot open. The desktop client folds typed item IDs to uppercase, so this is unreachable from the application — but the Transaction API applies no such conversion, accepts the lowercase ID, and creates the item; opening it in Item Maintenance / Item Master Inquiry then crashes the client (the browser, in the web client). Normalize item_id yourself before every create. Independently confirmed from production experience and deliberately not re-tested — the item it creates cannot be cleaned up through the UI. Added to Item Service Gotchascommunity session, documented by @mrwuss - feat: Async has no cancel pathAsync Operations now records that the immediate 202 acknowledges queueing only, not validation or success (a transaction the synchronous endpoint would reject returns a normal request ID), that the outcome and its business-rule text live only in the status GET, and that once queued, nothing stops the work: a loop that submits 50,000 wrong requests runs all 50,000, each firing the same DynaChange rules, alerts and event rules. The real hazard is the loss of backpressure — a payload bug that a synchronous run surfaces on record one surfaces here after the batch has landed. Probed live (26.1, 2026-08-19): every cancel-shaped route 404s, and DELETE /api/v2/transaction/async returns 405 — the route exists, for POST only — community session, verified and documented by @mrwuss - feat: Report windows are still windows — added to PDF Report Generation: the API user needs access to the report window; DynaChange data-change rules apply (sites cap date ranges so nobody runs a report wide open, and pdfreport hits those same caps, so a payload that works for one user can fail for another); and a wide-open run has been reported executing three days before exhausting server memory, where cancelling the HTTP request does not stop the server-side generation. Also adds a third report-name discovery path to sit beside the window_x_menu query — right-click any field in the report window → SQL Help, which names the m_* window directly — community session, documented by @mrwuss - docs: "The Transaction API can't do notes" is right for Order, but not for the usual reason — the session attributes it to the Add Line Note wizard. The Order definition does publish note elements as ordinary List DataElements — LINE_NOTE.line_note and HDR_NOTE.hdr_note (both keyed note_id, with note/topic/notepad_class_desc) plus TP_ITEMNOTES.tp_itemnotes (keyed note_uid) — so the wizard is incidental. Tested live (26.1, 2026-08-19): they are all disabled. Every column of both note elements is refused one at a time (Column is disabled: topic, then notepad_class_desc, then note); TP_ITEMNOTES refuses a step earlier with Tab page is disabled and cannot be selected. And IgnoreDisabled: true makes it worse — the same LINE_NOTE write returns Succeeded: 1 and the note is still empty on read-back, a third service exhibiting Breaking Changes entry 8. The standalone ItemNotepad/CustomerNotepad/SupplierNotepad services close the same way: their mandatory area selector is a drag-and-drop control — omit it and the save fails with You must select at least one area where this note will display., send it as a Selected Areas row and it fails with Column is disabled: area — the drag-and-drop limitation with a paper trail. Notes are Interactive API territory; use the verified notepad path. Recorded in Limitations@mrwuss - docs: Transaction API limitations extended — wizards launched from another window (the PO wizard and assembly decoder from Order Entry; credit-card entry, which is an embedded merchant-gateway form rather than a P21 window, so the documented TAPI path takes a token generated elsewhere), drag-and-drop windows, and mandatory notes, which strand a transaction on the notes tab and can be turned into prompts by a user setting on the API user's profile — with the caution that mandatory notes usually exist for a reason — community session, documented by @mrwuss - docs: UDT quote characters belong to the SQL-injection filter, and the behavior is version-dependentSQL Keyword False Positives now records that the protection is reported to be a string replacement rather than parameterization, so ' and " inside ordinary values are caught too. The everyday casualty is dimensional data (6" hose, 1/2" fitting, 3' section). Epicor is reported to have relaxed the logic since, and the community forum testing was done on ~25.1 — so the entry tells you to insert one row with the real punctuation on your version and read it back rather than trusting any document, this one included. Not testable here — the test tenant has no user-created UDT, and creating one is UI-only — community session, documented by @mrwuss


2026-08-19 — v1.5.1

Community contribution: the /api/sales/orders family creates orders as well as reading them.


2026-08-11 — v1.5.0

Cleared issues #103 and #105–#107, converted every API-calling code example in the docs into a complete runnable program, and re-verified a batch of long-standing claims against a live tenant. Verification build: 26.1.5910.3. The checks turned up several errors in our own documentation; those are listed first.

First pass — claims checked while resolving the issues

Second verification pass — every remaining unproven claim chased

Audited against two production integrations

Read the P21 integration code of two systems that run these APIs daily and compared every behavioural claim they encode against this documentation.

Repo conventions and indexes

Runnable examples

Issue work

2026-07-21 — v1.4.0

Cleared the open findings backlog (issues #98–#102): five live-verified 26.1 discoveries written into the docs, with four new service definitions fetched from the 26.1 play tenant (build 26.1.5894.1). All new cross-links validated against the generator's heading ids.

2026-07-14 — v1.3.0

Production moved to 2026.1, so every 2026.1 claim in these docs was re-tested against a live 2026.1 tenant (build 26.1.5894.1) rather than the 5873.1 test build they were written from. Epicor's Prophet 21 Release 2026.1 Release Guide was also mined for API-relevant content — it yielded exactly one new API surface, which is now documented.

2026-07-10 — v1.2.0

2026-07-10 — v1.1.1

End-to-end alignment audit: nine area agents checked every tracked file against the session's live-verified findings; ~118 findings triaged and fixed in one pass (PR #94). Highlights:

2026-07-10 — v1.1.0

Example-layout reorg: the repo now serves all four consumption styles symmetrically — Python, C#, JSON, XML.

2026-07-10 — v1.0.0

First tagged release. This wave cross-checked the docs against a community process playbook — every disputed claim was live-verified against a 25.2 test tenant — and restructured the repo for progressive disclosure: task routing, a schema library, a recipes cookbook, end-to-end example files, and payload-correctness tooling. Verified findings credit: Alex Westemeier.

Corrections (our docs were wrong):

New verified content:

Structure & tooling:

2026-07-06

2026-06-15

2026-05-22

2026-04-16

2026-04-10

2026-04-04

2026-03-06

2026-02-25

2026-02-17

2026-02-16

2026-02-13

2026-02-12

2026-02-11

2026-02-09

2026-01-20

2026-01-19

2026-01-02

2025-12-27

2025-12-26

2025-12-25 — Initial Release


Contributors

Contributor GitHub Contributions
@mrwuss @mrwuss Project creator, all documentation, HTML generation, maintenance
Claude Jones @RadAJones Reusable P21 API client with sync/async support (PR #16)
Sibin Francis @sibinfrancisaj Inventory REST API documentation (PR #23)
NextTWis @NextTWis Postman Collection for P21 API verification (PR #24)
Rob Landham @roblandham POST /api/sales/orders/ order creation — payload shape, required trailing slash, Lines.list nesting (issue #108)
Yeshaya Kohn @yeshayak Batch Pricing — JSON and XML bodies, the Content-Type trap behind its "XML only" reputation, and the bare <ArrayOfItemPriceInfo> namespace trap; the 25 Enterprise/Global Search views; the serverinfo version endpoint, which corrected doc 14's "no version endpoint exists"; the build-dependence of Monitoring/shortversion (PR #150). w_message dialogs are drivable via GET/POST /tools — this documentation had called them unanswerable in three places and in both response-window example programs since January (PR #155)
Alex Westemeier @AWestemeier Original author of much of the verified material in docs 03, 04 and 12, and of the recipes cookbook and schema-library patterns — report-service discovery, upsert semantics and Keys, IgnoreDisabled, Item nested location edits, the production order lifecycle, the buy-side build → receive → vouch cycle, the in-window wizard, and the ui/full surface
Jeff Poss PDF Report Generation endpoint discovery
Felipe Maurer Entity API taxonomy correction, UDT Service API discovery and testing, Inventory pricing endpoints, Stored Procedure Executor UID discovery, DynaChange enforcement in TAPI, Keys/row-collapse and /transaction/get session material (v1.6.0)
Kevin Landry Stored Procedure Executor execution via Interactive API
Brad Vandenbogaerde UDT database tables and SaaS hostname fix, SP Executor database tables, Visual Rule response/callback TAPI limitation
Justin Cassidy DynaChange as root cause for "Column is disabled" errors
Neil Timmerman TAPI HTTP 200 response validation gotcha
John Kennedy UDT Service SQL keyword issue, Inventory pricing URL-encoding testing (forward-slash limitation)
Jon Christie UDT response format quirk, TabName: null pattern for tabless response windows
David Sokoloski UDT P21 help docs reference, first discovered the 25.2 DatawindowName 4-parameter workaround
Jeff Patterson Confirmed the 25.2 DatawindowName fix, PO Receiving Group and ConvertPOToVoucher reports