Using your API

If you haven't already configured your app, see Configuring my APP

Authentication

The API uses JWT (JSON Web Token) authentication. First, sign in to get an access token:

POST /api/auth/signin
Content-Type: application/json

{
  "TenantId": "Demo",
  "Username": "admin",
  "Password": "Admin@123"
}

This returns an access token and refresh token. Include the access token in the Authorization header for all API requests:

GET /api/Customer
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Third-party clients such as MCP connectors don't sign in with a password — they get delegated, scoped access through OAuth 2.1. The API accepts both token formats on the same endpoints.

API Examples

All content sent via the body should be in json format

# Get all product
GET /api/Product

# Get a specific product
GET /api/Product(123e4567-e89b-12d3-a456-426614174000)

# Create a new product
POST /api/Product
Content-Type: application/json
{
  "SKU": "ADJWRE",
  "SalesDescription": "Adjustable Wrench",
  "Cost": 24.65
}

# Update a product
PATCH /api/Product(123e4567-e89b-12d3-a456-426614174000)
Content-Type: application/json
{
  "Cost": 22.85
}

# Delete a product
DELETE /api/Product(123e4567-e89b-12d3-a456-426614174000)

Natural-key references

JSON writes can identify a reference by the target entity's [NaturalKey] value:

{
  "FirstName": "Alex",
  "LastName": "Smith",
  "MailingAddress": {
    "Street": "10 Main Street",
    "Country": "US"
  }
}

Here, Country resolves to the existing country's GUID before the contact is saved. This also works for references inside collection objects and for writes sent through $batch. Missing keys and keys matching multiple records return validation errors. GUID references continue to work. A GUID-shaped string is treated as a GUID first.

OData Query Examples

# Filter product
GET /api/Product?$filter=Cost lt 25.00

# Sort product
GET /api/Product?$orderby=Sku

# Select specific fields
GET /api/Product?$select=Sku,SalesDescription,Cost

# Expand related entities (References or collections)
GET /api/Product?$expand=Uoms($select=Name)

# Combine queries
GET /api/Product?$filter=Cost lt 25.00&$orderby=Sku&$top=10&$select=Sku,SalesDescription

# Count
GET /api/Products?$count=true

# Aggregates
GET api/SalesOrderDetail?$apply=filter(Product/Sku eq 'EAST')/aggregate(Quantity with sum as QuantityOfEasterEggers)

A few tips:

$select

  • Entities and properties are case sensitive.
  • If select is not specified, then only the Guid property is returned.
  • All entities have a Guid and a Title property. $select=Guid,Title
  • Both virtual and persisted properties can be selected. However, returning only persisted properties is more performant.

$filter, $orderby, $apply, and $compute

  • For database-backed entities, filtering, ordering, grouping, aggregation, and computation must use persisted properties as inputs.
  • This includes property paths through related entities and expressions inside $apply.
  • Virtual (computed) properties remain readable through $select and $expand. Using them in these server operations returns HTTP 400 Bad Request with a validation error.
  • Aliases computed from supported persisted-property expressions remain usable; they are not virtual entity properties.
  • You can do a joined filter like this: Customer?$filter=PrimaryContact/MailingAddress/State eq 'PA'

$expand

  • Expand is used for both collections and references: SalesOrder?$expand=Details($select=Description) SalesOrder?$expand=PriceLevel($select=Name)
  • Expand and select multiple levels: Customer?$select=Id&$expand=PrimaryContact($select=FullName;$expand=MailingAddress($select=City,State))

$top and skip

  • Used for paged queries

OData Batch

Use POST /api/$batch to send several OData requests in one HTTP request.

Dashboards use $batch for read-only GET requests. Import tools can also use it for normal OData writes, such as POST, PATCH, and DELETE. Batched writes go through the same generated controllers as normal writes, so permissions, feature checks, deserialization hooks, business logic, and save logic still run.

The default batch limit is 64 parts. If an import has more work than that, split it into smaller batches. The same limit also applies to the number of write operations inside one changeset.

Workflows

Have the client interact with entities on the server by sending each change to the API and receiving updated entities. The server stages each change in durable workflow state until the workflow is committed, so edits survive a restart and any server can continue the workflow. See Workflows

See also

OData documentation: Getting Started · OData - the Best Way to REST

OData Aggregates: Grouping and Aggregation in OData Client - OData | Microsoft Learn