API · Api

Acumatica REST API — The Definitive Guide

A complete, field-tested reference to the Acumatica contract-based REST API: authentication, contracts, endpoints, batching, versioning, webhooks and the failure modes you only learn in production.

John Kihiu12 min read

Acumatica exposes two web services APIs — an older SOAP-based, screen-contract API and the contract-based REST API. This guide covers the REST one, which is what almost every new integration should be built against: it's the one Acumatica actively extends, the one that supports OAuth 2.0, and the one with a Swagger/OpenAPI schema you can generate per endpoint.

What "contract-based" means

The REST API doesn't expose your database tables. It exposes endpoints — named, versioned contracts you define (or accept the defaults for) on the Web Service Endpoints screen. Each endpoint maps entities like SalesOrder or Contact to specific DACs, fields, and actions, filtered through the same business logic and field-level security the UI enforces. A request to /entity/Default/24.200.001/SalesOrder runs through the same graph and validations as a user typing into the Sales Orders screen — that's the whole point of contract-based over a raw data API.

The practical consequence: if a field isn't in the endpoint's contract, the API can't see it, no matter how visible it is in the database. Extending an endpoint to add a custom field is normal, routine work, not an edge case.

Authentication: OAuth 2.0 vs. the older cookie session

Acumatica supports two auth models for the REST API. The original approach logs in against /entity/auth/login with a username and password, gets a session cookie back, and reuses that cookie for subsequent calls until it expires or you call /entity/auth/logout. It still works, but it's stateful, ties your integration to a single web node in a load-balanced deployment, and doesn't fit well with any client that can't hold onto cookies cleanly.

OAuth 2.0 is the model Acumatica now recommends for anything long-lived: register a connected application on the instance, exchange credentials at the identity server's token endpoint (typically {instance}/identity/connect/token), and send the resulting bearer token on every request. It's stateless per request, works cleanly behind a load balancer, and the token has an expiry you refresh explicitly rather than an opaque session timeout.

HTTP · TOKEN REQUEST
POST /identity/connect/token HTTP/1.1
Host: yourinstance.acumatica.com
Content-Type: application/x-www-form-urlencoded

grant_type=password&client_id={client_id}&client_secret={client_secret}
&username={api_user}&password={api_user_password}&scope=api

Anatomy of a request

Every entity call is scoped by endpoint name and version: /entity/{endpointName}/{version}/{entity}. Default is the built-in endpoint Acumatica ships; most serious integrations define a custom endpoint name so version bumps to the built-in default don't silently change your contract underneath you. GET reads a filtered list or a single record by key; PUT creates or updates (there is no separate POST-for-create — more on that below); DELETE removes a record where deletion is permitted by business logic.

One endpoint per integration, not one endpoint for everything

It's tempting to add every entity you'll ever need to a single custom endpoint. In practice, giving each integration (accounting sync, e-commerce, EDI) its own endpoint name means you can version and modify one without touching the contract another integration depends on.

Reading, filtering, and paging results

GET requests accept OData-style query parameters — $filter, $select, $expand, $top, and $skip. Page through a large result set by requesting a fixed $top and incrementing $skip by that same amount each call; when a response comes back with fewer records than $top, you've hit the last page. There's no cursor or continuation token in the contract-based API the way some modern REST APIs offer — skip/top is what you get, which means a record inserted mid-page-through can shift your window. For anything that must be exhaustive and consistent, filter on a monotonic field (like LastModifiedDateTime) rather than relying on raw offset paging alone.

Writing data: PUT-based upserts

There's no dedicated "create" verb. PUT with no key fields in the body creates a new record; PUT with key fields that match an existing record updates it. This single-verb model is convenient for idempotent retries — replaying the same PUT after a timeout doesn't create a duplicate the way a naive POST might — but it means your integration code has to be careful about which fields it includes. Omitting a field on an update generally leaves it unchanged, but sending an empty value is different from not sending the field at all, and getting that distinction wrong is a common source of "why did this field get cleared out" bugs.

Custom endpoints and versioning

Endpoints are defined and versioned from System > Integration > Web Service Endpoints. Cloning the default endpoint into a custom one with its own version number is the standard way to add fields, rename entities, or expose a Generic Inquiry as a queryable entity — without waiting on or being broken by an Acumatica platform upgrade that revs the built-in Default contract. Treat your custom endpoint version the same way you'd treat a package version: bump it deliberately, and keep old versions live until every consumer has migrated off them.

Wrapping up

The REST API's contract-based design is what makes it safe to build against: fields and validations you don't explicitly expose can't leak, and a custom endpoint insulates you from platform upgrades touching the built-in contract. Start with OAuth 2.0 over the cookie session, give each integration its own endpoint, and treat paging and PUT semantics as the two places integration bugs actually hide.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.