Part 2 of 4: POST & PATCH Operations in Business Central
In Part 1, we learned how to read product data from Business Central using GET requests. But reading data is only half the story. The real power of APIs comes when you can create and update data programmatically.
Today, we’ll follow Arjun as he adds 50 new products from a Delhi supplier and witness how Rajesh automates his entire Diwali sale pricing strategy.
The Business Scenario: New Supplier, New Products
Arjun discovered a new supplier in Delhi offering 50 unique gift items perfect for the festive season. In the old world, this would mean:
- Calling his father and dictating each product’s details over the phone
- Rajesh manually entering 50 products into Business Central
- Checking for typos and errors
- Updating the website separately
- Updating the mobile app separately
- Estimated time: 4-5 hours of tedious work
With Business Central APIs, Arjun could add all 50 products directly into the system from his location in Delhi.
New estimated time: 15 minutes
Creating Products via API: POST Requests
Understanding POST Requests
A POST request creates a new record in Business Central. Unlike GET requests that only read data, POST requests send data to the server to create something new.
Creating a Single Product
Let’s start simple – creating one product manually to understand the process.
Request:
HTTP POST /api/mehtagifts/shop/v1.0/companies(company-id)/products
Content-Type: application/json
Authorization: Bearer {access-token}
{
"itemNo": "GIFT051",
"description": "Crystal Photo Frame",
"description2": "Premium crystal frame with gold border",
"unitPrice": 890.00,
"inventory": 15,
"itemCategoryCode": "GIFTS",
"unitOfMeasure": "PCS"
}
Response:
Json {
"@odata.context": "https://api.businesscentral.dynamics.com/v2.0/production/api/mehtagifts/shop/v1.0/$metadata#companies(company-id)/products/$entity",
"@odata.etag": "W/\"JzQ0O0VnQUFBQUo3QlRVQU1BQXdBREFBTUE7MDA7Jw==\"",
"id": "7b8c9d0e-1f2a-3b4c-5d6e-7f8a9b0c1d2e",
"itemNo": "GIFT051",
"description": "Crystal Photo Frame",
"description2": "Premium crystal frame with gold border",
"unitPrice": 890.00,
"inventory": 15,
"itemCategoryCode": "GIFTS",
"unitOfMeasure": "PCS",
"lastModifiedDateTime": "2024-10-20T16:45:00Z"
}
Key Points About POST Requests:
- Content-Type header must be application/json
- Authorization header contains your access token
- Request body contains the product data in JSON format
- Response includes the newly created product with its generated ID
- Read-only fields (like id and lastModifiedDateTime) are automatically generated
Bulk Product Upload: The Real Power
Creating 50 products one by one through individual API calls would still be tedious. This is where automation shines.
Preparing the Data
Arjun created an Excel file with all 50 products:
ItemNumberNameDetailedDescriptionPriceStockCategoryUnitGIFT051Crystal Photo FramePremium frame with gold border89015GIFTSPCSGIFT052Marble Pen StandHandcrafted marble pen holder65020OFFICEPCSGIFT053Wooden Key HolderWall-mounted teak key organizer42030HOMEPCS…………………
PowerShell Script for Bulk Upload
Rohan wrote a PowerShell script to automate the upload:
powershell# Configuration
$tenantId = "your-tenant-id"
$clientId = "your-client-id"
$clientSecret = "your-client-secret"
$environment = "production"
$companyId = "your-company-guid"
$publisher = "mehtagifts"
$group = "shop"
$version = "v1.0"
Base URL
$baseUrl = "https://api.businesscentral.dynamics.com/v2.0/$environment/api/$publisher/$group/$version"
$apiUrl = "$baseUrl/companies($companyId)/products"
Get Access Token
$tokenBody = @{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://api.businesscentral.dynamics.com/.default"
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -Body $tokenBody
$accessToken = $tokenResponse.access_token
Set Headers
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
Import Excel Data (requires ImportExcel module)
Install-Module ImportExcel -Scope CurrentUser -Force
$products = Import-Excel -Path "C:\Temp\NewProducts.xlsx"
Upload Products
foreach ($product in $products) {
$productData = @{
itemNo = $product.ItemNumber
description = $product.Name
description2 = $product.DetailedDescription
unitPrice = [decimal]$product.Price
inventory = [int]$product.Stock
itemCategoryCode = $product.Category
unitOfMeasure = $product.Unit
} | ConvertTo-Json
try {
$response = Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $productData
Write-Host "✓ Added: $($product.Name) - Item No: $($response.itemNo)" -ForegroundColor Green
}
catch {
Write-Host "✗ Failed: $($product.Name) - Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host “`Upload Complete!”
Script Output:
✓ Added: Crystal Photo Frame – Item No: GIFT051
✓ Added: Marble Pen Stand – Item No: GIFT052
✓ Added: Wooden Key Holder – Item No: GIFT053
✓ Added: Decorative Wall Clock – Item No: GIFT054
✓ Added: Handwoven Jute Basket – Item No: GIFT100
Upload Complete!
Time taken: 3 minutes
All 50 products were now in Business Central, automatically available on the website, mobile app, and in-store POS system. No manual data entry. No typos. No duplicate work.
Updating Products: The Diwali Sale Challenge
October arrived with the festive season. Rajesh planned a Diwali sale offering 20% discount on all festival items. Manually updating 80 product prices would take hours and risk errors.
More importantly, the sale needed to start at exactly 12:01 AM on the first day of Diwali. Rajesh couldn’t stay up all night updating prices manually.
Understanding PATCH Requests
PATCH requests update existing records. Unlike POST (which creates new records), PATCH modifies specific fields of an existing record.
Updating a Single Product
Let’s see how to update one product’s price:
Request:
PATCH http /api/mehtagifts/shop/v1.0/companies(company-id)/products(5f8a7b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c)
Content-Type: application/json
Authorization: Bearer {access-token}
If-Match: *
{
"unitPrice": 360.00
}
Important Headers for PATCH
If-Match:
This header is REQUIRED for PATCH operations.
The asterisk (*) means “update regardless of current version.” You can also use a specific ETag value for optimistic concurrency control:
If-Match: W/”JzQ0O0VnQUFBQUo3QlRVQU1BQXdBREFBTUE7MDA7Jw==”
Without the If-Match header, your PATCH request will fail with a 400 error.
The Automated Diwali Sale Script
Rohan created a PowerShell script that would:
- Find all festival products
- Calculate 20% discount
- Update each product’s price
- Log the changes
- Schedule to run at exactly 12:01 AM
PowerShell
Configuration (reuse from previous script)
$tenantId = "your-tenant-id"
$clientId = "your-client-id"
$clientSecret = "your-client-secret"
$environment = "production"
$companyId = "your-company-guid"
$baseUrl = "https://api.businesscentral.dynamics.com/v2.0/$environment/api/mehtagifts/shop/v1.0"
$apiUrl = "$baseUrl/companies($companyId)/products"
Get Access Token
$tokenBody = @{
grant_type = "client_credentials"
client_id = $clientId
client_secret = $clientSecret
scope = "https://api.businesscentral.dynamics.com/.default"
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -Body $tokenBody
$accessToken = $tokenResponse.access_token
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
"If-Match" = "*"
}
Get all festival products
$filterUrl = "$apiUrl?`$filter=itemCategoryCode eq 'FESTIVAL'"
$festivalProducts = Invoke-RestMethod -Uri $filterUrl -Headers $headers
Write-Host "Found $($festivalProducts.value.Count) festival products`n" -ForegroundColor Cyan
Apply 20% discount to each product
foreach ($product in $festivalProducts.value) {
$originalPrice = $product.unitPrice
$discountedPrice = [Math]::Round($originalPrice * 0.80, 2)
$updateData = @{
unitPrice = $discountedPrice
} | ConvertTo-Json
$productUrl = "$apiUrl($($product.id))"
try {
$response = Invoke-RestMethod -Uri $productUrl -Method Patch -Headers $headers -Body $updateData
$discount = $originalPrice - $discountedPrice
Write-Host "✓ $($product.description)" -ForegroundColor Green
Write-Host " Item No: $($product.itemNo)" -ForegroundColor Gray
Write-Host " Price: ₹$originalPrice → ₹$discountedPrice (Save ₹$discount)" -ForegroundColor Yellow
Write-Host ""
}
catch {
Write-Host "✗ Failed to update $($product.description): $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host “`
Diwali Sale Prices Applied Successfully!” -ForegroundColor Green
Script Output:
Found 80 festival products
✓ Decorative Diya Set
Item No: GIFT001
Price: ₹450.00 → ₹360.00 (Save ₹90.00)
✓ Rangoli Stencil Kit
Item No: GIFT005
Price: ₹280.00 → ₹224.00 (Save ₹56.00)
✓ LED String Lights
Item No: GIFT010
Price: ₹320.00 → ₹256.00 (Save ₹64.00)
Diwali Sale Prices Applied Successfully!
Time taken: 30 seconds
The entire operation completed in 30 seconds, updating 80 products with perfect accuracy. The website, mobile app, and in-store systems all reflected the new prices instantly.
Scheduling the Script
To run the script at exactly 12:01 AM on Diwali day:
PowerShell
Create a scheduled task
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\DiwaliSale.ps1"
$trigger = New-ScheduledTaskTrigger -Once -At "2024-11-01 00:01"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount
Register-ScheduledTask -TaskName "DiwaliSaleStart" -Action $action -Trigger $trigger -Principal $principal
Rajesh slept peacefully, knowing the sale would start automatically at the exact moment he wanted.
Reverting Prices After the Sale
After Diwali, Rajesh needed to restore original prices. The same script was easily modified:
powershell
Restore original prices (increase by 25% to reverse 20% discount)
foreach ($product in $festivalProducts.value) {
$salePrice = $product.unitPrice
$originalPrice = [Math]::Round($salePrice * 1.25, 2)
$updateData = @{
unitPrice = $originalPrice
} | ConvertTo-Json
$productUrl = "$apiUrl($($product.id))"
Invoke-RestMethod -Uri $productUrl -Method Patch -Headers $headers -Body $updateData
Write-Host "✓ Restored: $($product.description) - ₹$salePrice → ₹$originalPrice"
}
Error Handling and Validation
Real-world API integrations need robust error handling. Here’s an enhanced version with better error management:
powershellfunction Invoke-ApiWithRetry {
param(
[string]$Uri,
[string]$Method,
[hashtable]$Headers,
[string]$Body,
[int]$MaxRetries = 3
)
$attempt = 0
$success = $false
while (-not $success -and $attempt -lt $MaxRetries) {
try {
$attempt++
$response = Invoke-RestMethod -Uri $Uri -Method $Method -Headers $Headers -Body $Body
$success = $true
return $response
}
catch {
$statusCode = $_.Exception.Response.StatusCode.value__
if ($statusCode -eq 429) {
# Rate limited - wait and retry
Write-Host "Rate limited. Waiting 60 seconds..." -ForegroundColor Yellow
Start-Sleep -Seconds 60
}
elseif ($statusCode -eq 401) {
# Unauthorized - refresh token
Write-Host "Token expired. Refreshing..." -ForegroundColor Yellow
$script:accessToken = Get-NewAccessToken
$Headers["Authorization"] = "Bearer $script:accessToken"
}
elseif ($attempt -eq $MaxRetries) {
throw "Failed after $MaxRetries attempts: $($_.Exception.Message)"
}
else {
# General error - wait and retry
Write-Host "Attempt $attempt failed. Retrying..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
}
}
}
}
# Usage
try {
$response = Invoke-ApiWithRetry -Uri $productUrl -Method "Patch" -Headers $headers -Body $updateData
Write-Host "✓ Updated successfully" -ForegroundColor Green
}
catch {
Write-Host "✗ Update failed: $($_.Exception.Message)" -ForegroundColor Red
# Log to error file
Add-Content -Path "C:\Logs\api-errors.log" -Value "$(Get-Date) - $($_.Exception.Message)"
}
Data Validation Before Upload
Before uploading products, validate the data:
powershellfunction Test-ProductData {
param($Product)
$errors = @()
# Required fields
if ([string]::IsNullOrWhiteSpace($Product.ItemNumber)) {
$errors += "Item Number is required"
}
if ([string]::IsNullOrWhiteSpace($Product.Name)) {
$errors += "Name is required"
}
# Price validation
if ($Product.Price -le 0) {
$errors += "Price must be greater than 0"
}
# Stock validation
if ($Product.Stock -lt 0) {
$errors += "Stock cannot be negative"
}
# Item number format (example: must be 7-10 characters)
if ($Product.ItemNumber.Length -lt 7 -or $Product.ItemNumber.Length -gt 10) {
$errors += "Item Number must be 7-10 characters"
}
return $errors
}
# Validate before uploading
foreach ($product in $products) {
$validationErrors = Test-ProductData -Product $product
if ($validationErrors.Count -gt 0) {
Write-Host "✗ Validation failed for $($product.Name):" -ForegroundColor Red
foreach ($error in $validationErrors) {
Write-Host " - $error" -ForegroundColor Red
}
continue
}
# Upload if validation passes
# ... (upload code here)
}
The Measurable Impact
After implementing these automation capabilities, Mehta Gifts saw:
Time Savings:
- Product entry: 5 minutes per product → 10 seconds per product (95% reduction)
- Bulk price updates: 3 hours → 2 minutes (99% reduction)
- Sale preparation: 4-5 hours → 15 minutes scheduled task
- Data accuracy: 85% → 99.5% (virtually eliminated human errors)
Business Benefits:
- Sales promotions could be executed instantly
- New product launches became faster and easier
- Seasonal pricing changes were effortless
- Staff could focus on customer service instead of data entry
- Real-time accuracy across all sales channels
What We’ve Learned
In this part, we’ve covered:
- Creating products with POST requests
- Bulk uploads using PowerShell and Excel
- Updating products with PATCH requests
- Automated price management
- Error handling and retry logic
- Data validation before upload
- Scheduled task automation
What’s Next?
In Part 3, we’ll build even more sophisticated integrations:
- Customer Loyalty API for omnichannel rewards
- Sales Order API for seamless e-commerce integration
- Real-time inventory synchronization with webhooks
- Multi-channel order processing
The foundation is solid. You can now read, create, and update data through Business Central APIs. But the real magic happens when we connect multiple systems in real-time.
Coming Up in Part 3: “Building Customer Experiences: Loyalty & Sales Order APIs” – where we’ll see how Mrs. Sharma’s loyalty points work seamlessly across all channels, and how a corporate order flows from website to warehouse automatically.