Consume Microsoft standard Business Central APIs (v2.0) - no AL coding required
—
—
Does it follow best practices?
Impact
—
No eval scenarios have been run
—
The risk profile of this skill
Standard APIs are maintained by Microsoft and expose common BC entities. No AL coding required - just authenticate and call the endpoints.
For custom data or business logic, use al-custom-api skill instead.
| Entity | Endpoint | Operations |
|---|---|---|
| Companies | /companies | GET |
| Customers | /customers | GET, POST, PATCH, DELETE |
| Vendors | /vendors | GET, POST, PATCH, DELETE |
| Items | /items | GET, POST, PATCH, DELETE |
| Sales Orders | /salesOrders | GET, POST, PATCH, DELETE |
| Sales Invoices | /salesInvoices | GET, POST, PATCH, DELETE |
| Purchase Orders | /purchaseOrders | GET, POST, PATCH, DELETE |
| Purchase Invoices | /purchaseInvoices | GET, POST, PATCH, DELETE |
| General Ledger Entries | /generalLedgerEntries | GET |
| Accounts | /accounts | GET |
| Dimensions | /dimensions | GET |
| Employees | /employees | GET, POST, PATCH, DELETE |
| Journal Lines | /journalLines | GET, POST, PATCH, DELETE |
Full list: Microsoft API v2.0 Reference
Prerequisite: Azure AD App Registration with Dynamics 365 Business Central API permissions.
See al-oauth-integration skill for token acquisition patterns.
Token endpoint:
POST https://login.microsoftonline.com/<TenantID>/oauth2/v2.0/token
grant_type=client_credentials
client_id=<ClientID>
client_secret=<ClientSecret>
scope=https://api.businesscentral.dynamics.com/.defaultBase URL pattern:
https://api.businesscentral.dynamics.com/v2.0/<TenantID>/<Environment>/api/v2.0With company:
https://api.businesscentral.dynamics.com/v2.0/<TenantID>/<Environment>/api/v2.0/companies(<CompanyID>)/<Entity>| Placeholder | Description | Example |
|---|---|---|
<TenantID> | Azure AD tenant ID (GUID) | 12345678-1234-1234-1234-123456789abc |
<Environment> | BC environment name | Production, Sandbox |
<CompanyID> | Company SystemId (GUID) | 87654321-4321-4321-4321-cba987654321 |
<Entity> | API entity name (camelCase plural) | customers, salesOrders |
<EntityID> | Record SystemId (GUID) | 11111111-2222-3333-4444-555555555555 |
GET all companies:
GET /api/v2.0/companies
Authorization: Bearer <token>GET all customers (with $select):
GET /api/v2.0/companies(<CompanyID>)/customers?$select=id,number,displayName,email
Authorization: Bearer <token>GET single customer:
GET /api/v2.0/companies(<CompanyID>)/customers(<EntityID>)
Authorization: Bearer <token>POST new customer:
POST /api/v2.0/companies(<CompanyID>)/customers
Authorization: Bearer <token>
Content-Type: application/json
{
"displayName": "New Customer",
"email": "customer@example.com",
"currencyCode": "EUR"
}PATCH update customer:
PATCH /api/v2.0/companies(<CompanyID>)/customers(<EntityID>)
Authorization: Bearer <token>
Content-Type: application/json
If-Match: *
{
"email": "updated@example.com"
}DELETE customer:
DELETE /api/v2.0/companies(<CompanyID>)/customers(<EntityID>)
Authorization: Bearer <token>
If-Match: *$select - Limit returned fields (ALWAYS use for performance):
?$select=id,number,displayName$filter - Filter results:
?$filter=displayName eq 'Contoso'
?$filter=lastModifiedDateTime gt 2024-01-01T00:00:00Z
?$filter=contains(displayName,'Smith')$filter with IN operator (BC24+):
Requires $schemaversion=2.1 in URL:
?$schemaversion=2.1&$filter=number in ('10000', '20000', '30000')Without $schemaversion=2.1, returns error BadRequest_MethodNotImplemented.
$expand - Include related entities:
?$expand=salesOrderLines
?$expand=customer($select=displayName,email)Filter inside $expand:
?$expand=salesOrderLines($filter=lineType eq 'Item')Note: Use parentheses () around the nested query options.
Multi-level expand:
?$expand=salesOrderLines($expand=item($expand=itemCategory))$orderby - Sort results:
?$orderby=displayName
?$orderby=lastModifiedDateTime desc$count - Get total count:
/salesOrders/$countReturns integer count of matching records.
BC uses server-driven paging with @odata.nextLink:
{
"@odata.context": "...",
"value": [...],
"@odata.nextLink": "https://api.../customers?$skiptoken=..."
}Pattern:
value array@odata.nextLink exists, request that URL@odata.nextLinkDo NOT use $top + $skip for paging - poor performance.
| Environment | Limit |
|---|---|
| BC Online | 600 requests/minute |
| BC On-Premises | Configurable |
HTTP 429 = Too Many Requests → implement exponential backoff.
| Error | Cause | Fix |
|---|---|---|
| 401 | Invalid/expired token | Refresh OAuth token |
| 403 | Missing permissions | Check Azure AD API permissions |
| 404 | Entity not found | Verify CompanyID and EntityID |
| 400 | Invalid request | Check JSON body, field names (camelCase) |
| 429 | Rate limited | Implement backoff, reduce request frequency |
displayName not Display Name* or actual ETagSee references/ folder for:
http-examples.md - Complete HTTP request examples for all standard entities (customers, vendors, items, sales orders, etc.)