POS Integration API
A robust API for managing pharmacy product inventory - improving inventory data quality, accuracy and pharmacy operations.
Base URL
https://ingest.medaki.ca
Authentication
All requests require API key authentication via headers:
X-API-Key: Your API keyX-Signature: HMAC SHA-256 signature of the request payload
Signature Generation
The signature is generated using HMAC SHA-256 with your secret key:
const payload = JSON.stringify(requestBody);
const signature = hmacSHA256(payload, secretKey);
Endpoints
Health Check
Check the API and database health status.
GET /health
Response:
{
"status": 200,
"success": true,
"message": "ok",
"data": {
"database": "healthy",
"timestamp": "2024-03-15T10:30:00.000Z"
},
"elapsed_ms": 45
}
Get Products
Retrieve products with pagination and filtering options.
GET /products
Query Parameters:
pharmacy_id(string, required): Pharmacy identifiergtin(string, optional): Filter by specific GTINskip(number, optional): Number of records to skip for pagination (default: 0)
Response:
{
"status": 200,
"success": true,
"data": [
{
"id": 1,
"gtin": "1234567890123",
"name": "Product Name",
"description": "Product description",
"price": 19.99,
"stock": 100
}
],
"pagination": {
"count": 1000,
"has_more": true,
"cursor": "next_page_token"
},
"elapsed_ms": 234
}
Create/Update Products (Asynchronous)
Queue products for creation or update. Returns immediately with a request ID for tracking.
POST /products
Request Body:
{
"pharmacy_id": "pharmacy123",
"medaki_pharmacy_id": "medaki456",
"action": "create",
"products": [
{
"gtin": "1234567890123",
"name": "Product Name",
"description": "Product description",
"brand": "Brand Name",
"old_name": "Previous Product Name",
"old_gtin": "0987654321098",
"category": "Category",
"image_url": "https://example.com/image.jpg",
"stock": 100,
"price": 19.99,
"sale_price": 15.99,
"distributor": "Distributor Name"
}
]
}
Response:
{
"status": 202,
"success": true,
"data": {
"message": "Product ingest queued for processing",
"requestId": "12345678",
"totalProducts": 1500,
"chunks": 3,
"statusUrl": "/operations/12345678/status"
},
"elapsed_ms": 45,
"request_id": "12345678"
}
Check Operation Status
Monitor the progress of asynchronous product operations.
GET /operations/{requestId}/status
Response:
{
"status": 200,
"success": true,
"data": {
"requestId": "12345678",
"status": "processing",
"totalChunks": 3,
"completedChunks": 2,
"totalProducts": 1500,
"processedProducts": 1000,
"failedProducts": 0,
"progress": 67,
"startTime": 1647329400000,
"lastUpdate": 1647329430000
}
}
Status Values:
queued: Operation queued but not yet startedprocessing: Currently processing chunkscompleted: All chunks processed successfullyfailed: Operation failed with errors
Product Schema
API Input Fields (Client Request)
Required for Create:
gtin: Global Trade Item Number (string)name: Product name (string)brand: Product brand (string)stock: Stock quantity (number)price: Product price (number)sale_price: Sale price (number)
Required for Update:
gtin: Global Trade Item Number (string)
Optional Fields:
old_gtin: Previous GTIN for updates (string)old_name: Previous name for updates (string)description: Product description (string)category: Product category (string)image_url: Product image URL (string)distributor: Distributor name (string)
Get Machine Operations
Retrieve recent operations for the authenticated machine.
GET /operations/machine
Query Parameters:
limit(number, optional): Number of operations to return (1-100, default: 50)
Response:
{
"status": 200,
"success": true,
"data": {
"machine_id": "b0586d19-4c05-4543-9bc8-ac9232a3f566",
"operations": [
{
"request_id": "12345678",
"status": "completed",
"total_chunks": 3,
"completed_chunks": 3,
"total_products": 1500,
"processed_products": 1500,
"failed_products": 0,
"start_time": 1647329400000,
"last_update": 1647329430000,
"end_time": 1647329450000,
"error": null,
"progress": 100
}
],
"total": 1,
"limit": 50
},
"elapsed_ms": 45,
"request_id": "87654321"
}
Retry Failed Operation
Manually retry a failed operation by re-queuing failed chunks.
POST /operations/{requestId}/retry
Response:
{
"status": 200,
"success": true,
"data": {
"message": "Operation retry initiated successfully",
"request_id": "12345678",
"failed_chunks_retried": 2,
"retry_attempt": 1
},
"elapsed_ms": 45,
"request_id": "12345678"
}
Error Responses
Authentication Error
{
"status": 401,
"success": false,
"error": "Invalid API key",
"request_id": "12345678"
}
Validation Error
{
"status": 400,
"success": false,
"error": "Validation failed: gtin is required",
"request_id": "12345678"
}
Duplicate GTIN Error
{
"status": 400,
"success": false,
"error": "Duplicate GTINs found in payload: 1234567890123, 9876543210987",
"request_id": "12345678"
}
Operation Not Found
{
"status": 404,
"success": false,
"error": "Operation not found",
"request_id": "12345678"
}
Database Timeout Error
{
"status": 408,
"success": false,
"error": "Database operation timed out",
"timeout_ms": 12000,
"request_id": "12345678"
}
Request Too Large
{
"status": 413,
"success": false,
"error": "Request too large. Maximum size allowed: 10485760 bytes (10 MB)",
"provided_size": 15728640,
"max_size": 10485760,
"request_id": "12345678"
}
Database Unavailable
{
"status": 503,
"success": false,
"error": "Database is currently unavailable",
"request_id": "12345678"
}
Server Error
{
"status": 500,
"success": false,
"error": "Internal server error",
"request_id": "12345678"
}
Rate Limits & Constraints
- Maximum request size: 10 MB
- Maximum products per request: 25,000
- No timeout limits (asynchronous processing)
- Immediate response with tracking capabilities
Workflow Example
# 1. Submit large batch
POST /products
→ 202 Accepted { requestId: "abc123", chunks: 10 }
# 2. Monitor progress
GET /operations/abc123/status
→ { status: "processing", progress: 60 }
# 3. Check completion
GET /operations/abc123/status
→ { status: "completed", processedProducts: 5000 }
Performance Notes
- Chunk processing: ~2-5 seconds per 500 products
- Queue throughput: Up to 3 concurrent chunks per operation
- Database timeouts: 12 seconds with automatic retry
- Status updates: Real-time
- Retry attempts: Up to 3 retries with exponential backoff and dead letter queue backup
Error Handling & Retry Logic
The API implements intelligent error classification and retry logic:
- Timeout Errors: Automatically retried with 2x longer delays
- Connection Errors: Retried with exponential backoff
- Authentication Errors: Not retried (permanent failure)
- Validation Errors: Not retried (permanent failure)
- Database Constraint Errors: Not retried (permanent failure)
Code Examples
JavaScript/Node.js Authentication
const crypto = require('crypto');
async function sha256Hash(data, key) {
const encoder = new TextEncoder();
const encodedData = encoder.encode(data);
const encodedKey = encoder.encode(key);
const cryptoKey = await crypto.subtle.importKey('raw', encodedKey, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, encodedData);
return Array.from(new Uint8Array(signature))
.map((byte) => byte.toString(16).padStart(2, '0'))
.join('');
}
const body = JSON.stringify({
pharmacy_id: "123",
medaki_pharmacy_id: "med123",
action: "create",
products: [/* product data */]
});
const signature = await sha256Hash(body, secretKey);
const headers = {
'X-API-Key': publicKey,
'X-Signature': signature,
'Content-Type': 'application/json'
};
cURL Examples
# Health Check
curl -X GET https://ingest.medaki.ca/health
# Get Products
curl -X GET \
"https://ingest.medaki.ca/?pharmacy_id=123&skip=0" \
-H "X-API-Key: your_public_key" \
-H "X-Signature: generated_signature"
# Create Products (Async)
curl -X POST \
https://ingest.medaki.ca/ \
-H "Content-Type: application/json" \
-H "X-API-Key: your_public_key" \
-H "X-Signature: generated_signature" \
-d '{
"pharmacy_id": "123",
"medaki_pharmacy_id": "med123",
"action": "create",
"products": [
{
"gtin": "1234567890123",
"name": "Product Name",
"brand": "Brand Name",
"stock": 100,
"price": 29.99,
"sale_price": 24.99
}
]
}'
# Check Operation Status
curl -X GET \
"https://ingest.medaki.ca/operations/12345678/status" \
-H "X-API-Key: your_public_key" \
-H "X-Signature: generated_signature"
# Get Machine Operations
curl -X GET \
"https://ingest.medaki.ca/operations/machine?limit=10" \
-H "X-API-Key: your_public_key" \
-H "X-Signature: generated_signature"
# Retry Failed Operation
curl -X POST \
"https://ingest.medaki.ca/operations/12345678/retry" \
-H "X-API-Key: your_public_key" \
-H "X-Signature: generated_signature"
