Entity API Overview¶
The Entity API is the primary RESTful CRUD interface for Prophet 21. It provides direct access to P21 business entities — customers, vendors, orders, inventory, accounting records, and more — through a consistent set of HTTP endpoints. For most external integrations, the Entity API is the simplest and most direct path to reading and writing P21 data.
What Is the Entity API¶
- RESTful CRUD — standard GET, POST, PUT operations map to read, create, and update
- Direct entity access — each endpoint maps to a P21 business object (customer, order, vendor, etc.)
- Supports User-Defined Fields (UDFs) — custom fields added to entities appear automatically in the XML response
- XML primary format — all requests and responses use XML by default
- Bearer token authentication — standard OAuth 2.0 bearer tokens
No DELETE
The Entity API does not expose DELETE operations. Record removal in P21 is typically handled through status changes (e.g., marking a record inactive) rather than hard deletion.
Base URL¶
All entity endpoints are relative to this base. Replace {your-server} with your P21 application server hostname or IP.
Standard CRUD Operations¶
Every entity in the Entity API follows the same URL pattern and supports the same set of operations.
| Method | URL Pattern | Description |
|---|---|---|
GET | /entity/{entityName}/ | List all records (returns ArrayOf{Entity}) |
POST | /entity/{entityName}/ | Create a new record |
GET | /entity/{entityName}/{companyId}_{entityId} | Get a specific record by compound key |
PUT | /entity/{entityName}/{companyId}_{entityId} | Update a specific record |
GET | /entity/{entityName}/new | Get a blank record with defaults populated |
GET | /entity/{entityName}/ping | Health check for the endpoint |
Compound Key Format¶
Many P21 entities use a two-part compound key in the URL: {companyId}_{entityId}. The underscore _ is the separator.
Both parts are required. The company ID is typically 01 for single-company installations.
Always Use /new Before Creating Records¶
Do not skip this step
Before POSTing a new record, always call the /new endpoint first. Skipping this step is the most common source of creation failures, because required fields with system-generated defaults will be missing from your payload.
The /new endpoint returns a fully-formed blank record with all required fields pre-populated with P21 defaults. This serves three purposes:
- Populates required defaults — fields like
date_created,company_id, and various flags are set by P21 business logic - Shows you the complete field structure — you can inspect exactly which fields the entity supports, including UDFs
- The response object is ready to modify and POST back — take the XML, change what you need, and POST it as-is
The response is a fully valid Customer XML document. Modify the fields you need, then POST the entire document to /entity/customers/.
HTTP Headers¶
All requests to the Entity API require these headers:
For GET requests, Content-Type is not required, but Accept and Authorization always are.
Token Acquisition
Obtain your bearer token from the P21 authentication endpoint before making Entity API calls. Tokens are time-limited. Implement token refresh logic in long-running integration processes.
URL Pattern Reference¶
| Segment | Description | Example |
|---|---|---|
{server} | P21 application server hostname | p21.example.com |
{entityName} | Entity collection name (lowercase plural) | customers, orders, vendors |
{companyId} | Company identifier | 01 |
{entityId} | Entity-specific primary key | C100001, V200042, 10043 |
Health Check (Ping)¶
Every entity endpoint supports a ping operation that confirms the endpoint is reachable and the service is healthy.
The response is always:
Use this in monitoring scripts and integration startup checks before executing business logic.
User-Defined Fields (UDFs)¶
P21 supports custom UDF fields added to entities through the system administration UI. These fields appear automatically in the XML schema alongside standard fields.
- UDFs are included in both GET responses and the
/newtemplate - They are writable via POST and PUT just like standard fields
- Field name prefixes vary by entity (e.g.,
ud_is common but not universal) - The
/newendpoint is the most reliable way to discover available UDFs for an entity
Discovering UDFs
Call /entity/{entityName}/new and inspect the response XML. Any fields beyond the standard schema are UDFs specific to your P21 installation.
List Operations¶
GET /entity/{entityName}/ returns an ArrayOf{Entity} wrapper:
<ArrayOfCustomer>
<Customer>
<company_id>01</company_id>
<customer_id>C100001</customer_id>
<!-- ... -->
</Customer>
<Customer>
<company_id>01</company_id>
<customer_id>C100002</customer_id>
<!-- ... -->
</Customer>
</ArrayOfCustomer>
Performance on large datasets
The list endpoint returns all records without pagination by default. For entities with large record counts (orders, inventory items), avoid calling the list endpoint in production without filtering. Use the P21 Query API or OData endpoints for filtered, paginated access to large datasets.
Error Handling¶
| HTTP Status | Meaning | Action |
|---|---|---|
200 OK | Success — entity data in response body | Deserialize and process |
400 Bad Request | Malformed request XML or invalid field values | Inspect response body for field-level error details |
401 Unauthorized | Missing or expired bearer token | Re-authenticate and retry |
404 Not Found | Entity record does not exist for the given key | Verify companyId and entityId values |
422 Unprocessable Entity | Business rule validation failure | Inspect response body for P21 validation messages |
500 Internal Server Error | Server-side error | Check P21 application logs |
Error responses include a body with detail. Always read the response body on non-2xx status codes — P21 typically returns a structured XML error that identifies the failing field or rule.
Common Integration Pattern (C#)¶
public class P21EntityClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public P21EntityClient(HttpClient httpClient, string baseUrl)
{
_httpClient = httpClient;
_baseUrl = baseUrl;
_httpClient.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
}
private void SetAuthHeader(string token)
{
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}
private async Task<T> GetNewTemplateAsync<T>(string entityName)
{
var response = await _httpClient.GetAsync($"{_baseUrl}/entity/{entityName}/new");
response.EnsureSuccessStatusCode();
var xml = await response.Content.ReadAsStringAsync();
var serializer = new XmlSerializer(typeof(T));
using var reader = new StringReader(xml);
return (T)serializer.Deserialize(reader);
}
private string SerializeToXml<T>(T obj)
{
var serializer = new XmlSerializer(typeof(T));
using var writer = new StringWriter();
serializer.Serialize(writer, obj);
return writer.ToString();
}
}