A troubleshooting reference for the Priority OData REST API - why errors come back as XML from a JSON API, the auto-unique key trap on PATCH, batch limits, authentication combinations that cancel each other out, and how to find real field names.
Key takeaways
- Errors come back as XML even though the API is JSON. The useful text is inside an InterfaceErrors element - if your client only parses JSON responses, every validation failure looks like a parse error.
- You cannot PATCH a record by its auto-unique key. Use the regular unique key. This one silently produces "record not found" on records that plainly exist.
- Basic authentication stops working the moment External ID access is enabled. An integration that ran for a year can break on an unrelated ERP configuration change.
- The $metadata endpoint is the answer to almost every "what is this field called" question. Read it from the actual installation, because customised forms and fields differ between sites.
The Priority REST API is built on OData and works well once you understand it, but a small set of behaviours costs hours on every first project. This is what is worth knowing before you start debugging rather than after.
For planning and scoping an integration project, see integrating with the Priority API. For pushing events out instead of polling, see Priority webhooks.
The URL shape - and what breaks in it
The base URL is:
https://<server>/odata/Priority/<tabula.ini>/<environment>/<FORM>Three of these are a source of mistakes: tabula.ini is the tabula file the installation uses, and environment is the company's internal name in Priority - not the display name. Both differ between clients, and between test and production at the same client.
Guessing them wastes time. Ask whoever maintains the installation.
The most confusing error: XML from a JSON API
You send JSON, expect JSON, and get XML back. Validation errors return as a <FORM> element containing <InterfaceErrors>:
<InterfaceErrors>
<text>Specify 'N' or 'Y' as the default value...</text>
</InterfaceErrors>The practical problem: if your client tries to parse every response as JSON, every validation failure looks like a parse error, and the real message - which is usually precise and helpful - gets thrown away.
What to do: check the response content type, and when it is not JSON, extract the text from InterfaceErrors and log it verbatim. That message tells you exactly which field was rejected and why.
Keys: where this breaks
Single and composite keys
A record is addressed in parentheses after the entity name:
GET /FAMILY_LOG('765')
GET /AINVOICES(IVNUM='T9696',IVTYPE='A',DEBIT='D')A composite key separates components by commas, each named explicitly. Omitting one component does not produce a clear error - it simply fails to find the record.
The trap: PATCH by auto-unique key
This is probably the most frustrating error on a first project. You cannot update a record by its auto-unique key. Use the regular unique key instead.
Why it is frustrating: the GET returns the record with the auto-unique key in it, you naturally use that key in the PATCH, and the result is a failure that looks like the record does not exist. If you see "not found" on a record you just read, this is almost always the reason.
Subforms
Subform rows are written by navigating from the parent:
POST /ORDERS('SO18000002')/ORDERITEMS_SUBFORM
PATCH /ORDERS('SO18000002')/ORDERITEMS_SUBFORM(1)You can also create parent and children in a single request by nesting the subform array in the body:
{
"CUSTNAME": "007",
"ORDERITEMS_SUBFORM": [
{ "PARTNAME": "111-001", "DUEDATE": "2016-08-01T00:00:00+03:00" }
]
}This is the recommended way to create a document: one atomic request beats creating a header and then lines in separate calls, because a failure halfway through leaves a partial document in the system.
Authentication: three methods and one forbidden combination
| Method | Details |
|---|---|
| Basic | The default. Username and password of an active Priority user holding an API licence, from the Personnel File form. |
| Personal Access Token | From version 19.1. Managed in the REST Interface Access Tokens form, allowing multiple tokens per user. Supplied through the Basic Auth fields, but the username/password convention is not intuitive - verify it against the current documentation. |
| OAuth2 | For third-party or browser-based access. Requires the External ID module licence and uses an Authorization: Bearer header. |
The forbidden combination: you cannot use Basic Authentication while External ID access is enabled. This matters beyond setup - it means enabling External ID on the installation, for reasons entirely unrelated to you, will break an integration that has run for a year. Worth documenting with the client.
One more point: user permissions apply to the API. If the API user cannot see a form in the interface, they will not see it through the API either - and that manifests as missing records rather than a permission error.
Limits worth knowing up front
- Minimum version: the REST API is available from Priority 17.2 onward.
- Batching: up to 100 requests per call. Before version 21.0 the limit was 1,000. If you wrote an importer that relies on batches of 1,000 and the client upgraded, it is now broken.
- Field names: not intuitive, and not identical across installations.
$metadata - the biggest time saver
The /$metadata endpoint returns the full schema of that specific installation: entities, fields, types and relationships.
This matters especially in Priority because customised forms and fields differ between sites. Two clients on the same version can require completely different integration code. Any answer you find in a forum about a field name is correct for the installation of whoever wrote it, not necessarily yours.
So: before guessing a field name, pull $metadata from the client's installation and search it. It is also the correct way to discover which subforms exist and what they are called.
A debugging order that works
- Confirm the URL is right - particularly
tabula.iniand the environment name. - Verify the user holds an API licence and can see the form in the interface.
- Pull
$metadataand confirm the entity and field names. - GET a single record before attempting any write.
- If a write fails - read the XML. The text in
InterfaceErrorsis almost always the precise explanation.
Frequently asked questions
Why does the Priority REST API return XML errors when I send JSON?
Validation failures are returned as a FORM element containing InterfaceErrors, in XML, even though the API otherwise speaks JSON. If your HTTP client assumes every response is JSON, the parse fails and the real error text is discarded. Check the content type on error responses and extract the InterfaceErrors text - it usually names the exact field and the exact problem.
Why does PATCH return not found for a Priority record that exists?
Most likely you are addressing the record by its auto-unique key. The Priority REST API does not allow updating by an auto-unique key - you must use the regular unique key. The confusion arises because a GET returns the record with the auto-unique key present, so using it in the PATCH feels natural.
How many requests can a Priority API batch contain?
Up to 100 requests in a single batch call. In versions before 21.0 the limit was 1,000, so an importer written against an older installation can break after a client upgrade. Size your batches at 100 or below regardless of the version you are currently on.
Can I use Basic authentication with the Priority REST API?
Yes, it is the default method, using an active Priority user with an API licence. But it stops working when External ID access is enabled on the installation - the two cannot coexist. Since External ID may be turned on for unrelated reasons, this is a documented dependency worth raising with the client before go-live.
How do I find the real field names in a Priority installation?
Request the /$metadata endpoint on that specific installation. It returns the full schema - entities, fields, types and relationships. This is essential in Priority because customised forms and fields differ between sites, so a field name that works at one client may not exist at another even on the same version.
What is the minimum Priority version for the REST API?
Priority 17.2. Below that the REST API is not available and an integration has to use a different mechanism, which is a material difference in scope - so confirm the installed version before quoting the work.
Keep reading
Related service
Integrations
Make the systems you already pay for talk to each other.
About the author
Yehonatan Saadia
Freelance automation, web & MVP engineer
I'm Yehonatan Saadia, a senior engineer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build and I'll tell you the fastest reliable way to ship it.
