Select Page
Managing Data Through APIs: Creating, Updating & Bulk Operations

December 23, 2025

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.