Extracted Entities---Streaming Invoice Images and Bounding Regions Endpoints
Extracted Entities---Streaming Invoice Images and Bounding Regions Endpoints
Overview
This document outlines the implementation stories for two new endpoints that will stream invoice images directly and provide access to bounding region data associated with extracted entities. These endpoints will improve security by eliminating the need for client-side token management and optimize data storage by storing persistent blob URLs instead of time-limited URLs in the database.
---
Background & Context
Current State
-
The
ExtractedEntitiestable currently stores image URLs and bounding region URLs -
These URLs may be stored with query parameters (expiring tokens) or as persistent blob URLs
-
There’s no dedicated endpoint to stream images directly to the client
-
There’s no dedicated endpoint to fetch bounding region JSON data from blob storage
Business Value
-
Security: Stream images directly from API without exposing storage credentials or tokens
-
Data Integrity: Store persistent blob URLs instead of expiring URLs in the database
-
Flexibility: Separate concerns between data storage and data access
-
Client Experience: Provide clients with ready-to-use binary image data without manual token or URL management
Story 1: Stream Invoice Images
Story Description
As a client application consuming invoice extraction data
I want to stream invoice images directly from the API for an extracted entity
So that I can display images in the UI without managing blob storage authentication or token expiration
Technical Details
Endpoint Specification
Request Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| entityId | integer | Yes | The ExtractedEntityId |
| pageNumber | integer | No | Specific page number to retrieve (omit to get all pages) |
Response Schema (200 OK)
Single Page Request (GET /v1/extracted_entities/:entityId/images/:pageNumber):
Multiple Pages Request (GET /v1/extracted_entities/:entityId/images):
Error Responses
| Status Code | Scenario | Response Body |
|-------------|----------|---------------|
| 404 | Entity not found | { "error": "Entity not found" } |
| 404 | No images found | { "error": "No images found for this entity" } |
| 404 | Page number out of range | { "error": "Page number exceeds available pages" } |
| 500 | Blob download error | { "error": "Failed to retrieve images from storage" } |
| 500 | Internal error | { "error": "Internal Server Error" } |
Implementation Tasks
Task 1.1: Update Type Definitions
File: src/api/v1/extractedEntity/extractedEntities.types.ts
Task 1.2: Create Schema Definition
File: src/api/v1/extractedEntity/extractedEntities.schema.ts
Add new schemas:
Task 1.3: Implement Service Layer Logic
File: src/api/v1/extractedEntity/extractedEntities.service.ts
Add new methods:
Task 1.4: Implement Controller Layer
File: src/api/v1/extractedEntity/extractedEntities.controller.ts
Add new methods:
Task 1.5: Create Route Handlers
File: src/api/v1/extractedEntity/extractedEntities.route.ts
Add new routes:
Acceptance Criteria
Functional Requirements
-
✅ Endpoint streams binary image data directly to client
-
✅ Single page endpoint returns one image with proper content-type headers
-
✅ Multiple pages endpoint returns multipart response with all images
-
✅ Returns 404 if entity doesn’t exist
-
✅ Returns 404 with specific message if no images found for entity
-
✅ Returns 404 if page number exceeds available pages
-
✅ Database is updated to store clean blob URLs if URLs with query parameters were stored
-
✅ Response includes page number and total pages in headers
-
✅ Handles URLs with or without query parameters from database gracefully
-
✅ Images can be displayed directly in browser (Content-Disposition: inline for single page)
Non-Functional Requirements
-
✅ Endpoint requires authentication (bearer token)
-
✅ Endpoint requires appropriate scopes (
EXTRACTED_ENTITIES_READ) -
✅ Single image response time < 2 seconds per image
-
✅ Multiple images stream progressively (no buffering all images in memory)
-
✅ Supports images up to 10MB each
-
✅ Proper error logging for troubleshooting
-
✅ No sensitive data logged (PIDs, credentials)
-
✅ Memory efficient streaming (uses Node.js streams, not full buffering)
Testing Requirements
-
✅ Unit test for URL parsing logic (normalize URLs by removing query parameters)
-
✅ Unit test for service layer with mocked blob storage
-
✅ Integration test with actual blob storage (using Azurite)
-
✅ Test single image endpoint returns correct binary data and headers
-
✅ Test multiple images endpoint returns valid multipart response
-
✅ Test with entity containing no images (404 response)
-
✅ Test with non-existent entity (404 response)
-
✅ Test with invalid page number (404 response)
-
✅ Test database update when URLs with query parameters are normalized
-
✅ Test image content-type detection (PNG vs JPEG)
-
✅ Test multipart boundary parsing for clients
Documentation Requirements
-
✅ OpenAPI schema updated with new endpoints
-
✅ Generated OpenAPI docs include request/response examples
-
✅ Code comments explain streaming and multipart logic
-
✅ README updated with endpoint usage examples including UI rendering
---
Story 2: GET Bounding Regions JSON
Story Description
As a client application performing document analysis
I want to retrieve the bounding regions JSON file from blob storage for an extracted entity
So that I can display visual overlays, perform OCR validation, or enhance the user interface with region highlighting
Technical Details
Endpoint Specification
Request Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| entityId | integer | Yes | The ExtractedEntityId |
Response Schema (200 OK)
Error Responses
| Status Code | Scenario | Response Body |
|-------------|----------|---------------|
| 404 | Entity not found | { "error": "Entity not found" } |
| 404 | Bounding regions not found | { "error": "No bounding regions found for this entity" } |
| 500 | Blob download failed | { "error": "Failed to fetch bounding regions from storage" } |
| 500 | Invalid JSON in blob | { "error": "Invalid bounding regions data" } |
| 500 | Internal error | { "error": "Internal Server Error" } |
Implementation Tasks
Task 2.1: Update Type Definitions
File: src/api/v1/extractedEntity/extractedEntities.types.ts
Task 2.2: Create Schema Definition
File: src/api/v1/extractedEntity/extractedEntities.schema.ts
Add new schema:
Task 2.3: Implement Blob Storage Helper
File: src/helpers/blobStorage.ts
Add new function:
Task 2.4: Implement Service Layer Logic
File: src/api/v1/extractedEntity/extractedEntities.service.ts
Add new method:
Task 2.5: Implement Controller Layer
File: src/api/v1/extractedEntity/extractedEntities.controller.ts
Add new method:
Task 2.6: Create Route Handler
File: src/api/v1/extractedEntity/extractedEntities.route.ts
Add new route:
Acceptance Criteria
Functional Requirements
-
✅ Endpoint downloads and returns bounding regions JSON from blob storage
-
✅ Returns 404 if entity doesn’t exist
-
✅ Returns 404 with specific message if no bounding regions URL found
-
✅ Returns 500 if blob download fails or JSON is invalid
-
✅ Database is updated to store clean blob URL if URL with query parameters was stored
-
✅ Response includes the blob URL for reference
-
✅ Handles URLs with or without query parameters from database gracefully
-
✅ Properly parses and validates JSON structure before returning
Non-Functional Requirements
-
✅ Endpoint requires authentication (bearer token)
-
✅ Endpoint requires appropriate scopes (
EXTRACTED_ENTITIES_READ) -
✅ Response time < 5 seconds for JSON files up to 5MB
-
✅ Proper error logging for troubleshooting
-
✅ No sensitive data logged (PIDs, credentials)
-
✅ Handles large JSON files efficiently (streaming if > 10MB)
Testing Requirements
-
✅ Unit test for blob download helper function
-
✅ Unit test for URL parsing logic (normalize URLs by removing query parameters)
-
✅ Unit test for service layer with mocked blob storage
-
✅ Integration test with actual blob storage (using Azurite)
-
✅ Test with entity containing no bounding regions URL (404 response)
-
✅ Test with non-existent entity (404 response)
-
✅ Test with invalid JSON in blob (500 response)
-
✅ Test with non-existent blob (500 response)
-
✅ Test database update when URL with query parameters is normalized
Documentation Requirements
-
✅ OpenAPI schema updated with new endpoint
-
✅ Generated OpenAPI docs include request/response examples
-
✅ Code comments explain blob download and parsing logic
-
✅ README updated with endpoint usage examples
---
Story 3: Generate Updated OpenAPI Documentation
Story Description
As a API consumer or developer
I want the OpenAPI documentation to reflect the two new endpoints
So that I can understand the API contract and generate client SDKs
Implementation Tasks
Task 3.1: Verify Schema Completeness
File: src/api/v1/extractedEntity/extractedEntities.schema.ts
Ensure both new schemas are properly defined:
-
getImageUrlsschema -
getBoundingRegionsschema
Task 3.2: Generate OpenAPI Documentation
Command: Run the documentation generation script
Task 3.3: Validate Generated Documentation
File: docs/openapi.yaml and docs/openapi.json
Verify the following are present:
-
Both endpoints appear under the “Extracted Entities” tag
-
All request parameters are documented
-
All response schemas are complete
-
Error responses (404, 500) are documented
-
Security requirements are specified
Task 3.4: Test OpenAPI Specification
Tools: Use Swagger Editor or Postman to import and validate the OpenAPI spec
-
Import
docs/openapi.yamlinto Swagger Editor -
Verify no schema validation errors
-
Check that example values are reasonable
-
Test that all references are resolved correctly
Acceptance Criteria
Documentation Requirements
-
✅ OpenAPI 3.0 specification is valid (no errors in Swagger Editor)
-
✅ Both new endpoints appear in documentation
-
✅ All parameters have descriptions
-
✅ All response schemas are complete and accurate
-
✅ Security schemes are properly defined
-
✅ Examples are provided for request/response bodies
-
✅ Tags properly organize endpoints
-
✅ Generated documentation matches implementation
Testing Requirements
-
✅ OpenAPI spec validates against OpenAPI 3.0 schema
-
✅ Documentation can be imported into Postman without errors
-
✅ All endpoint paths are unique and correct
-
✅ Response codes match actual implementation
---
Cross-Story Considerations
Security
-
Both endpoints require authentication via bearer token
-
Both endpoints require
EXTRACTED_ENTITIES_READscope -
Images streamed directly through authenticated API (no token exposure to client)
-
No credentials or sensitive data logged
-
Blob URLs without query parameters are stored in database (non-expiring)
Performance
-
Image streaming parallelized for multipart responses
-
Blob download uses efficient buffering for binary data
-
Database updates are conditional (only when URL normalization needed)
-
Response times:
- Single Image: < 2 seconds per image
- Multiple Images (multipart): < 5 seconds for 10 images
- Bounding Regions: < 5 seconds for 5MB JSON
Error Handling
-
Consistent error response format across endpoints
-
Specific error messages for different failure scenarios
-
Proper HTTP status codes (404, 500)
-
Error logging without exposing sensitive details
-
Graceful degradation when blob storage is unavailable
Data Migration
If existing entities have URLs with query parameters in the database:
-
First API call will normalize URLs to persistent blob URLs
-
Database update is transactional
-
Original functionality preserved (streaming works regardless of stored URL format)
-
No downtime required for migration
Testing Strategy
-
Unit Tests: Service layer logic with mocked dependencies
-
Integration Tests: Full request/response cycle with Azurite
-
E2E Tests: Real scenarios with database + blob storage
-
Load Tests: Performance validation with concurrent requests
---
Implementation Order
Phase 1: Foundation (Story 1 - Stream Images)
-
Update type definitions
-
Create schema definitions (two endpoints)
-
Implement service layer
-
Implement controller layer
-
Create route handlers
-
Write unit tests
-
Write integration tests
Phase 2: Bounding Regions (Story 2)
-
Update type definitions
-
Create schema definition
-
Implement blob download helper
-
Implement service layer
-
Implement controller layer
-
Create route handler
-
Write unit tests
-
Write integration tests
Phase 3: Documentation (Story 3)
-
Verify schema completeness
-
Generate OpenAPI docs
-
Validate documentation
-
Update README with examples
---
Testing Plan
Unit Tests
Location: src/api/v1/extractedEntity/tests/
Test Files to Create:
-
extractedEntities.imageStreaming.test.ts -
extractedEntities.boundingRegions.test.ts -
blobStorage.download.test.ts(insrc/helpers/tests/)
Test Cases for Image Streaming:
-
✅ Parse blob URL correctly
-
✅ Parse URLs with query parameters correctly (extract blob path)
-
✅ Stream single image with correct headers
-
✅ Stream multiple images as multipart response
-
✅ Handle entity not found (404)
-
✅ Handle no images found (404)
-
✅ Handle invalid page number (404)
-
✅ Update database when URLs with query parameters detected
-
✅ Don’t update database when clean blob URLs already stored
-
✅ Handle blob storage errors gracefully
-
✅ Detect correct content-type (PNG vs JPEG)
Test Cases for Bounding Regions:
-
✅ Download and parse valid JSON from blob
-
✅ Handle entity not found (404)
-
✅ Handle no bounding regions URL (404)
-
✅ Handle blob download failure (500)
-
✅ Handle invalid JSON in blob (500)
-
✅ Update database when URL with query parameters detected
-
✅ Don’t update database when clean blob URL already stored
-
✅ Handle large JSON files (streaming)
Integration Tests
Location: src/api/v1/extractedEntity/tests/
Test Scenarios:
-
Happy Path - Single Image Stream:
- Create test entity with blob URLs
- Call GET /extracted_entities/:id/images/1
- Verify binary image data returned
- Verify headers (Content-Type, X-Page-Number, X-Total-Pages)
-
Happy Path - Multiple Images Stream:
- Create test entity with multiple blob URLs
- Call GET /extracted_entities/:id/images
- Verify multipart response structure
- Verify all images received
- Parse multipart boundaries correctly
-
Happy Path - Bounding Regions:
- Upload test JSON to Azurite
- Create test entity with blob URL
- Call GET /extracted_entities/:id/boundingRegions
- Verify JSON returned
-
Migration Path - URL Normalization:
- Create entity with URLs containing query parameters
- Call image streaming endpoint
- Verify database updated with clean blob URLs
-
Error Scenarios:
- Non-existent entity
- Missing images/bounding regions
- Blob storage unavailable
- Invalid JSON in blob
Load Testing
Tool: Artillery or K6
Scenarios:
-
100 concurrent requests to /images/:pageNumber endpoint
-
50 concurrent requests to /images endpoint (multipart)
-
100 concurrent requests to /boundingRegions endpoint
-
Mixed workload (image streaming + bounding regions)
-
Response time < 2s for p95 (single images)
-
Response time < 5s for p95 (multipart/multiple images)
---
Rollout Plan
Pre-Deployment Checklist
-
✅ All unit tests passing
-
✅ All integration tests passing
-
✅ Code review completed
-
✅ OpenAPI documentation generated and validated
-
✅ README updated with new endpoint examples
-
✅ Security review completed (no PII logged)
-
✅ Performance testing completed
Deployment Steps
-
Deploy to development environment
-
Run smoke tests
-
Deploy to QA environment
-
Run full test suite
-
Deploy to production with feature flag (if available)
-
Monitor error rates and response times
-
Enable for all users
Monitoring & Alerts
-
Track response times for all image streaming endpoints
-
Monitor error rates (404, 500)
-
Track database update frequency (URL normalization)
-
Alert on blob storage connection failures
-
Alert on response times > 5s
-
Monitor streaming throughput and memory usage
Rollback Plan
If issues arise:
-
Disable routes via feature flag
-
Revert deployment if necessary
-
Investigate errors using logs
-
Fix issues and redeploy
---
Dependencies & Prerequisites
Technical Dependencies
-
✅ Azure Blob Storage configured (
AZURE_STORAGE_CONNECTION_STRING_DOCAI) -
✅
@azure/storage-blobpackage installed -
✅ Existing
blobStorage.tshelper functions -
✅ Database access to
ExtractedEntitiestable -
✅ Authentication middleware (
fastify.authenticate) -
✅ Authorization middleware (
fastify.authorize)
Data Prerequisites
-
ExtractedEntities records must have image URLs or bounding region URLs in
extractedEntitiesPayload -
Blobs must exist in Azure Storage
-
JSON files must be valid and follow expected schema
Team Dependencies
-
Backend developer for implementation
-
QA engineer for testing
-
DevOps for deployment
-
Technical writer for documentation review
---
Success Metrics
Functional Metrics
-
✅ 100% of entities with images can stream binary image data
-
✅ 100% of entities with bounding regions can retrieve JSON data
-
✅ 100% of URLs in database normalized to persistent blob URLs after first access
Performance Metrics
-
✅ p95 response time < 2s for single image streaming endpoint
-
✅ p95 response time < 5s for multiple images (multipart) endpoint
-
✅ p95 response time < 5s for bounding regions endpoint
-
✅ 99.9% uptime for all endpoints
Quality Metrics
-
✅ 85%+ code coverage
-
✅ 0 critical security vulnerabilities
-
✅ 0 PII logged in production logs
---
Future Enhancements
Potential Improvements (Out of Scope for MVP)
-
Caching Layer: Cache image data in Redis for frequently accessed images to reduce blob storage calls
-
Batch Endpoint:
POST /extracted_entities/batch/imagesto get images for multiple entities -
Lazy Loading: Stream images one at a time with pagination for entities with many images
-
CDN Integration: Serve images through Azure CDN for faster delivery
-
Image Metadata: Return image dimensions, file size, format in additional headers
-
Bounding Regions Filtering: Query parameter to filter regions by confidence score or content
-
Webhooks: Notify clients when new images or bounding regions are available
-
Image Transformations: Resize, crop, or format conversion on-the-fly
-
Progressive JPEG: Stream images in progressive format for faster rendering
-
Thumbnail Generation: Generate and cache thumbnail versions for preview
---
Appendix
Example Request/Response
Example 1: Stream Single Image
Example 1b: Stream All Images (Multipart)
Example 2: Get Bounding Regions```bash
GET /v1/extracted_entities/12345/boundingRegions
Example 3: Entity Not Found
Example 4: No Images Found
Example 5: Page Number Out of Range
Example 6: UI Rendering Implementation
---
Glossary
| Term | Definition |
|------|------------|
| Blob URL | Persistent URL to a blob without query parameters or tokens (requires separate authentication) |
| Image Streaming | Server-side process of downloading images from blob storage and returning binary data directly to client |
| Multipart Response | HTTP response format that contains multiple parts (images) separated by boundaries |
| Entity | Record in ExtractedEntities table representing processed invoice data |
| Bounding Region | Rectangle coordinates identifying text location in a document image |
| Document Intelligence | Azure service for OCR and document analysis (formerly Form Recognizer) |
| Azurite | Local Azure Storage emulator for development and testing |
---
References
---
Change Log
| Date | Version | Changes | Author |
|------|---------|---------|--------|
| 2026-01-05 | 1.0 | Initial story design created | GitHub Copilot |
---
Document Status: ✅ Ready for Review
Next Step: Team review and story estimation
Estimated Total Story Points: 13 points (Story 1: 5pts, Story 2: 6pts, Story 3: 2pts)