- Oct 27, 2025
Smart Business Card Reader with Azure AI ,Azure Document Intelligence ,OpenAI and .NET Core
- DevTechie Inc
- Azure Computer Vision
In today’s fast-paced world, automating data extraction from documents like business cards can save hours of manual entry and improve data accuracy. In this article, we’ll walk through how to build a .NET Core API that:
• Accepts a business card image upload
• Extracts structured data using Azure Document Intelligence
• Maps the extracted fields to a C# model using Azure OpenAI
• Stores the result in a database
We’ll also cover how to test the API using Swagger or Postman.
Step 1: Azure Setup
Before diving into code, let’s configure the necessary Azure resources.
Azure AI Document Intelligence (formerly Form Recognizer)
This service extracts structured data from documents using prebuilt models.
Steps:
Go to Azure Portal
Click Create a resource
Search for Azure AI services
Select Azure AI services (Microsoft)
Click Create
Fill in:
Resource name: e.g.,
BusinessCardAIRegion: Choose a supported region (e.g., East US, West Europe)
Pricing tier: Standard S0
Click Review + Create, then Create
After deployment, go to the resource and copy:
Endpoint
Key 1
2. Azure OpenAI
This service enables access to GPT models for natural language processing.
Steps:
• Go to Azure Portal
• Click Create a resource
• Search for Azure OpenAI
• Select Azure OpenAI (Microsoft)
• Click Create
• Fill in:
• Resource name: e.g.,
• Region: Choose a region that supports GPT-4 (e.g., East US)
• After deployment:
• Go to Model Deployments
• Deploy the gpt-4.1 model
• Copy the Endpoint and Key
Step 2: .NET Core API Breakdown(find the complete code snippet at the end of the article)
Let’s walk through the key components of the BusinessCardController
Upload Endpoint
[HttpPost("upload")]
public async Task<IActionResult> UploadCard([FromForm] IFormFile image)Accepts a business card image (JPEG, PNG, or PDF)
• Saves it to a local folder
• Calls ExtractBusinessCardData to analyze the image
• Sends the extracted JSON to MapFieldsUsingAzureOpenAI for intelligent field mapping
• Deserializes the mapped result into BusinessCard model
• Saves it to the database
Document Intelligence Integration
var operation = await client.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-businessCard", stream);Uses the prebuilt-businessCard model to extract fields like name, company, email, phone, and address
• Converts the result into a flat dictionary using ConvertFieldsToJson
OpenAI Field Mapping
ChatCompletion result = await MapFieldsUsingAzureOpenAI(cardJsonString);Sends the extracted JSON and a C# model definition to GPT-4.1
• Prompts the model to:
• Map fields confidently
• Return a clean JSON object under finalMapping
• Include the original input under incomingJson
• Deserializes the mapped result into a BusinessCard object
Step 3: Testing the API with Swagger or Postman
Postman:
Below is the json response:
{
"incomingJson": {
"Addresses": [
"123 Anywhere St., Any City"
],
"ContactNames": [
"Daniel Gallego"
],
"Emails": [
"hello@reallygreatsite.com"
],
"JobTitles": [
"Fashion Designer"
],
"WorkPhones": [
"+123-456-7890"
]
},
"finalMapping": {
"Id": 0,
"Name": "Daniel Gallego",
"Company": "Fashion Designer",
"Email": "hello@reallygreatsite.com",
"Phone": "+123-456-7890",
"Address": "123 Anywhere St., Any City"
}
}Below is the screenshot from db:
Complete code snippet:
BusinessCardController
using Azure;
using Azure.Core;
using BusinessCardApi.Constants;
using BusinessCardApi.DataFactory;
using BusinessCardApi.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Azure.AI.DocumentIntelligence;
using Azure.AI.FormRecognizer.DocumentAnalysis;
using System.Text;
using Azure.AI.Vision.ImageAnalysis;
using Microsoft.Extensions.Options;
using System.Text.Json;
using System.Reflection;
using Azure.AI.OpenAI;
using OpenAI.Chat;
using System.Collections.Generic;
using System.Text.Json.Nodes;
namespace BusinessCardApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BusinessCardController : ControllerBase
{
private readonly BusinessCardDbContext _context;
private readonly IWebHostEnvironment _env;
public BusinessCardController(BusinessCardDbContext context, IWebHostEnvironment env)
{
_context = context;
_env = env;
}
/// <summary>
/// Upload a business card image and extract structured data using Azure Document Intelligence.
/// </summary>
/// <param name="image">Business card image (JPEG, PNG, or PDF)</param>
/// <returns>Extracted business card data</returns>
[HttpPost("upload")]
[ProducesResponseType(typeof(BusinessCard), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UploadCard([FromForm] IFormFile image)
{
try
{
if (image == null || image.Length == 0)
return BadRequest("No image uploaded.");
var uploadsDir = Path.Combine(_env.ContentRootPath, "uploads");
Directory.CreateDirectory(uploadsDir);
var filePath = Path.Combine(uploadsDir, Guid.NewGuid() + Path.GetExtension(image.FileName));
using (var stream = new FileStream(filePath, FileMode.Create))
{
await image.CopyToAsync(stream);
}
var card = await ExtractBusinessCardData(filePath);
if (card == null)
return BadRequest("Could not extract business card data.");
string cardJsonString = JsonSerializer.Serialize(card);
ChatCompletion result = await MapFieldsUsingAzureOpenAI(cardJsonString);
var jsonObject = JsonSerializer.Deserialize<Dictionary<string, object>>(result.Content[0].Text);
var businessCard = JsonSerializer.Deserialize<BusinessCard>(jsonObject["finalMapping"].ToString());
_context.BusinessCards.Add(businessCard);
await _context.SaveChangesAsync();
return Ok(jsonObject);
}
catch (Exception ex)
{
return BadRequest(ex.Message.ToString());
}
}
public async Task<Dictionary<string, object>> ExtractBusinessCardData(string imagePath)
{
var client = new DocumentAnalysisClient(
new Uri(AzureAIConstants.DocumentAnalysisEndpoint),
new AzureKeyCredential(AzureAIConstants.DocumentAnalysisApiKey)
);
using var stream = System.IO.File.OpenRead(imagePath);
var operation = await client.AnalyzeDocumentAsync(WaitUntil.Completed, "prebuilt-businessCard", stream);
var result = operation.Value;
var card = new BusinessCard();
var values = ConvertFieldsToJson(result);
return values;
}
public Dictionary<string, object> ConvertFieldsToJson(Azure.AI.FormRecognizer.DocumentAnalysis.AnalyzeResult result)
{
var doc = result.Documents.FirstOrDefault();
if (doc == null) return new Dictionary<string, object>();
var jsonDict = new Dictionary<string, object>();
foreach (var field in doc.Fields)
{
var key = field.Key;
var value = field.Value;
if (value.FieldType == Azure.AI.FormRecognizer.DocumentAnalysis.DocumentFieldType.List)
{
var listItems = value.Value.AsList().Select(item => item.Content).ToList();
jsonDict[key] = listItems;
}
else if (value.FieldType == Azure.AI.FormRecognizer.DocumentAnalysis.DocumentFieldType.Dictionary)
{
var dictItems = new Dictionary<string, string>();
foreach (var kvp in value.Value.AsDictionary())
{
dictItems[kvp.Key] = kvp.Value.Content;
}
jsonDict[key] = dictItems;
}
else
{
jsonDict[key] = value.Content;
}
}
return jsonDict;// JsonSerializer.Serialize(jsonDict, new JsonSerializerOptions { WriteIndented = true });
}
public async Task<ChatCompletion> MapFieldsUsingAzureOpenAI(string extractedJson)
{
var prompt = $@"
Here is the fixed C# model:
public class BusinessCard {{
public int Id {{ get; set; }}
public string Name {{ get; set; }}
public string Company {{ get; set; }}
public string Email {{ get; set; }}
public string Phone {{ get; set; }}
public string Address {{ get; set; }}
}}
Here is the incoming JSON:
{extractedJson}
";
// Retrieve the OpenAI endpoint from environment variables
var endpoint = AzureAIConstants.AzureOpenAIEndpoint;
var key = AzureAIConstants.AzureOpenAIApiKey;
AzureKeyCredential credential = new AzureKeyCredential(key);
// Initialize the AzureOpenAIClient
AzureOpenAIClient azureClient = new(new Uri(endpoint), credential);
// Initialize the ChatClient with the specified deployment name
ChatClient chatClient = azureClient.GetChatClient("gpt-4.1");
// Create a list of chat messages
var messages = new List<ChatMessage>
{
new SystemChatMessage(@"You are a smart field-mapping engine."),
new UserChatMessage(@"Map the incoming Json and its value with given C# model and provide the output as
json of C# model with values, please give plain json response without any slashes
and in seperate property say finalMapping, also add incoming json in the response as seperate
property as incomingJson, also for properties from incoming json which cannot be mapped or
not very confident in mapping with C# model in such case match those properties to others prop
in c# model:"+prompt),
};
// Create chat completion options
var options = new ChatCompletionOptions
{
Temperature = (float)1,
MaxOutputTokenCount = 800,
TopP = (float)1,
FrequencyPenalty = (float)0,
PresencePenalty = (float)0
};
// Create the chat completion request
ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options);
return completion;
}
}
public class OpenAIResponse
{
public List<Choice> Choices { get; set; }
public class Choice
{
public Message Message { get; set; }
}
public class Message
{
public string Role { get; set; }
public string Content { get; set; }
}
}
}BusinessCardDbContext
using BusinessCardApi.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace BusinessCardApi.DataFactory
{
public class BusinessCardDbContext : DbContext
{
public BusinessCardDbContext(DbContextOptions<BusinessCardDbContext> options) : base(options) { }
public DbSet<BusinessCard> BusinessCards { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BusinessCard>().ToTable("BusinessCard");
}
}
}
BusinessCard Model:
using System.ComponentModel.DataAnnotations.Schema;
namespace BusinessCardApi.Model
{
public class BusinessCard
{
public int Id { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public string? Others { get; set; }
}
}
