Skip to main content

Overview

The FaxIntegrationService is a dedicated microservice that consolidates all fax operations into a single service. It handles both inbound faxes (receiving from RingCentral, storing in Azure Blob, publishing events) and outbound faxes (sending to pharmacies via RingCentral).
Release: v2.11.0 (December 17, 2025)This service was extracted from CurentaOrderTriagingCoreApi to improve separation of concerns and system reliability.

Key Responsibilities

Inbound Fax Processing:
ResponsibilityDescription
Webhook HandlingReceives and validates RingCentral fax webhook notifications
PDF DownloadDownloads fax PDF attachments from RingCentral API
Blob UploadUploads fax documents to Azure Blob Storage
Event PublishingPublishes FaxNotificationReceived event to Service Bus
Outbound Fax Sending:
ResponsibilityDescription
Pharmacy Fax SendingSends order documents to pharmacies via RingCentral
Delivery TrackingTracks fax delivery status with improved logging
Status ReportingReports delivery success/failure back to calling service

Service Metadata

PropertyValue
Repositoryhttps://github.com/AllCare-ai/FaxIntegrationService
Technology.NET 8
DatabaseNone (stateless)
Message BrokerAzure Service Bus
External APIsRingCentral, Azure Blob Storage

Architecture

High-Level Design

Inbound Data Flow

1

Webhook Received

RingCentral sends a webhook notification to FaxIntegrationService when a new fax arrives
2

Validation

Service validates the webhook payload and extracts fax metadata (message ID, sender, etc.)
3

PDF Download

Service authenticates with RingCentral API and downloads the fax PDF attachment
4

Blob Upload

PDF is uploaded to Azure Blob Storage with appropriate metadata
5

Event Published

FaxNotificationReceived event is published to Service Bus with blob URL and metadata
6

Downstream Processing

CurentaOrderTriagingCoreApi subscribes to the event and processes the fax for order triaging

Outbound Data Flow

1

Send Request

CurentaOrderTriagingCoreApi calls FaxIntegrationService with document and recipient details
2

Fax Transmission

FaxIntegrationService sends the fax to the pharmacy via RingCentral API
3

Delivery Tracking

Service monitors RingCentral for delivery status updates
4

Status Response

Delivery status (success/failure) is returned to the calling service

API Reference

Webhook Endpoint

POST /api/fax/webhook

Receives incoming fax notifications from RingCentral.
Anonymous Access — This endpoint allows anonymous access to receive RingCentral webhooks. Validation is performed via the Validation-Token header handshake.
Headers:
HeaderDescription
Validation-TokenRingCentral webhook validation token (returned in response for subscription verification)
Request Body:
{
  "uuid": "string",
  "event": "/restapi/v1.0/account/~/extension/~/fax?direction=Inbound",
  "timestamp": "2025-12-17T10:30:00.000Z",
  "subscriptionId": "string",
  "ownerId": "string",
  "body": {
    "id": "string",
    "type": "Fax",
    "direction": "Inbound",
    "from": {
      "phoneNumber": "+1234567890",
      "name": "Sender Name"
    },
    "to": [
      {
        "phoneNumber": "+0987654321",
        "name": "Recipient"
      }
    ],
    "attachments": [
      {
        "id": "string",
        "uri": "string",
        "type": "RenderedDocument",
        "contentType": "application/pdf"
      }
    ],
    "messageStatus": "Received",
    "faxPageCount": 3,
    "creationTime": "2025-12-17T10:30:00.000Z"
  }
}
Responses:
StatusDescription
200 OKWebhook processed successfully
200 OK with Validation-Token headerSubscription verification response
400 Bad RequestInvalid payload or processing failed

Outbound Fax Endpoint

POST /api/fax/send

Sends a fax to a pharmacy or other recipient.
Authentication Required — This endpoint requires JWT Bearer token authentication.
Headers:
HeaderDescription
AuthorizationBearer token for authentication
Content-Typeapplication/json
Request Body:
{
  "recipientFaxNumber": "+1234567890",
  "recipientName": "ABC Pharmacy",
  "documentUrl": "https://[storage].blob.core.windows.net/orders/order-12345.pdf",
  "orderId": "guid",
  "coverPageText": "Order for Patient: John Doe",
  "priority": "normal"
}
FieldTypeRequiredDescription
recipientFaxNumberstringYesDestination fax number (E.164 format)
recipientNamestringNoName of recipient for cover page
documentUrlstringYesURL to the PDF document to fax
orderIdguidNoAssociated order ID for tracking
coverPageTextstringNoText to include on fax cover page
prioritystringNonormal or high (default: normal)
Responses:
StatusDescription
200 OKFax queued successfully
400 Bad RequestInvalid request (missing fields, invalid fax number)
401 UnauthorizedMissing or invalid authentication
500 Internal Server ErrorRingCentral API error
Success Response:
{
  "success": true,
  "data": {
    "faxId": "string",
    "status": "Queued",
    "recipientFaxNumber": "+1234567890",
    "queuedAt": "2025-12-17T10:30:00.000Z"
  }
}
Error Response:
{
  "success": false,
  "error": {
    "code": "INVALID_FAX_NUMBER",
    "message": "The recipient fax number is invalid"
  }
}

GET /api/fax/status/

Gets the delivery status of a sent fax. Parameters:
NameTypeDescription
faxIdstringThe fax ID returned from the send endpoint
Response:
{
  "faxId": "string",
  "status": "Delivered",
  "recipientFaxNumber": "+1234567890",
  "queuedAt": "2025-12-17T10:30:00.000Z",
  "sentAt": "2025-12-17T10:31:00.000Z",
  "deliveredAt": "2025-12-17T10:32:00.000Z",
  "pageCount": 3,
  "duration": 45
}
Fax Status Values:
StatusDescription
QueuedFax is queued for sending
SendingFax is currently being transmitted
DeliveredFax was successfully delivered
FailedFax delivery failed
BusyRecipient line was busy
NoAnswerRecipient did not answer

Event Schema

FaxNotificationReceived

Published to Service Bus after successful fax ingestion.
{
  "messageId": "string",
  "blobUrl": "https://[storage-account].blob.core.windows.net/fax-documents/[filename].pdf",
  "senderPhoneNumber": "+1234567890",
  "senderName": "Sender Name",
  "recipientPhoneNumber": "+0987654321",
  "pageCount": 3,
  "receivedAt": "2025-12-17T10:30:00.000Z",
  "metadata": {
    "ringCentralMessageId": "string",
    "subscriptionId": "string"
  }
}

Configuration

Application Settings

{
  "RingCentral": {
    "ClientId": "[RC_CLIENT_ID]",
    "ClientSecret": "[RC_CLIENT_SECRET]",
    "JwtToken": "[RC_JWT_TOKEN]",
    "ReceiveExtensionId": "[EXTENSION_ID]"
  },
  "AzureStorage": {
    "ConnectionString": "[BLOB_CONNECTION_STRING]",
    "ContainerName": "fax-documents"
  },
  "ServiceBus": {
    "ConnectionString": "[SERVICE_BUS_CONNECTION_STRING]",
    "TopicName": "fax-notifications"
  }
}

Environment Variables

VariableDescriptionRequired
RingCentral__ClientIdRingCentral OAuth client IDYes
RingCentral__ClientSecretRingCentral OAuth client secretYes
RingCentral__JwtTokenRingCentral JWT for authenticationYes
RingCentral__ReceiveExtensionIdExtension ID for receiving faxesYes
AzureStorage__ConnectionStringAzure Blob Storage connection stringYes
AzureStorage__ContainerNameBlob container nameYes
ServiceBus__ConnectionStringAzure Service Bus connection stringYes
ServiceBus__TopicNameTopic for publishing eventsYes

Migration from CurentaOrderTriagingCoreApi

What Was Removed

The following components were removed from CurentaOrderTriagingCoreApi: Inbound Fax Processing:
ComponentPrevious LocationNew Location
RingCentral Webhook HandlerFaxController.ValidateWebhook()FaxIntegrationService
RingCentral Subscription SetupRinCentralSubscription.csFaxIntegrationService
PDF Download LogicRinCentralSubscription.GetAttachmentContent()FaxIntegrationService
Blob Storage UploadFaxService (inline)FaxIntegrationService
Outbound Fax Sending:
ComponentPrevious LocationNew Location
Pharmacy Fax SendingFaxSenderServiceFaxIntegrationService
RingCentral Send APIOrders service (inline)FaxIntegrationService

What Remains in CurentaOrderTriagingCoreApi

ComponentDescription
FaxControllerStill handles fax history, listing, and processing actions
FaxServiceStill processes faxes for order triaging after receiving events
FaxNotificationHubSignalR hub for real-time notifications (unchanged)
Service Bus ConsumerSubscribes to FaxNotificationReceived events
Fax Send ClientCalls FaxIntegrationService to send outbound faxes

Breaking Changes

None for end users. This is a backend refactor with no UI or behavior changes. Faxing works the same way from the UI.
For developers:
ChangeImpact
Webhook URL changeRingCentral subscription must point to FaxIntegrationService URL
Service Bus dependencyCurentaOrderTriagingCoreApi must subscribe to fax-notifications topic
Outbound fax APIOrders service must call FaxIntegrationService API instead of sending directly

Monitoring & Observability

Health Checks

EndpointDescription
GET /health/liveLiveness probe
GET /health/readyReadiness probe (checks RingCentral, Blob, Service Bus connectivity)

Key Metrics

Inbound Fax Metrics:
MetricDescription
fax.inbound.receivedCount of webhook notifications received
fax.inbound.downloadedCount of PDFs successfully downloaded
fax.inbound.uploadedCount of files uploaded to Blob Storage
fax.inbound.publishedCount of events published to Service Bus
fax.inbound.durationTime from webhook to event publish
Outbound Fax Metrics:
MetricDescription
fax.outbound.queuedCount of faxes queued for sending
fax.outbound.sentCount of faxes successfully sent
fax.outbound.deliveredCount of faxes confirmed delivered
fax.outbound.failedCount of fax delivery failures
fax.outbound.durationTime from queue to delivery

Logging

Key log events: Inbound:
[INF] Fax webhook received: MessageId={MessageId}
[INF] Downloading fax PDF: MessageId={MessageId}, AttachmentId={AttachmentId}
[INF] Uploaded to blob: MessageId={MessageId}, BlobUrl={BlobUrl}
[INF] Published event: MessageId={MessageId}
[ERR] Failed to download PDF: MessageId={MessageId}, Error={Error}
Outbound:
[INF] Fax send requested: FaxId={FaxId}, Recipient={RecipientNumber}
[INF] Fax queued: FaxId={FaxId}, RingCentralId={RcMessageId}
[INF] Fax delivered: FaxId={FaxId}, Duration={Duration}ms
[WRN] Fax delivery pending: FaxId={FaxId}, Status={Status}
[ERR] Fax send failed: FaxId={FaxId}, Error={Error}

Troubleshooting

Common Issues

Symptoms:
  • No fax events appearing in Service Bus
  • No webhook logs in FaxIntegrationService
Causes:
  • RingCentral subscription expired or pointing to wrong URL
  • Firewall blocking inbound requests
Solution:
  1. Verify RingCentral subscription is active and pointing to correct URL
  2. Check network/firewall rules allow inbound traffic
  3. Manually trigger subscription renewal
Symptoms:
  • Webhook received but no blob uploaded
  • Error logs: “Failed to download PDF”
Causes:
  • RingCentral token expired
  • Network timeout
  • Invalid message/attachment ID
Solution:
  1. Check RingCentral JWT token is valid
  2. Verify network connectivity to RingCentral API
  3. Check message ID exists in RingCentral
Symptoms:
  • PDF downloaded but event not published
  • Error logs: “Failed to upload to blob”
Causes:
  • Invalid storage connection string
  • Container doesn’t exist
  • Insufficient permissions
Solution:
  1. Verify Azure Storage connection string
  2. Ensure container exists and is accessible
  3. Check managed identity or SAS token permissions
Symptoms:
  • Events published but not processed
  • Faxes not appearing in triaging queue
Causes:
  • Subscription not created
  • Dead-letter queue full
  • Deserialization errors
Solution:
  1. Verify Service Bus subscription exists
  2. Check dead-letter queue for failed messages
  3. Validate event schema matches consumer expectation
Symptoms:
  • Fax queued but never sent
  • Status remains “Queued” indefinitely
Causes:
  • RingCentral API rate limiting
  • Invalid recipient fax number
  • Document URL inaccessible
Solution:
  1. Check RingCentral rate limits and quotas
  2. Validate fax number format (E.164)
  3. Verify document URL is accessible from FaxIntegrationService
  4. Check RingCentral account has fax sending permissions
Symptoms:
  • Fax status shows “Failed”, “Busy”, or “NoAnswer”
  • Pharmacy not receiving faxes
Causes:
  • Recipient fax machine issues
  • Invalid or disconnected fax number
  • Network/line quality issues
Solution:
  1. Verify recipient fax number is correct and active
  2. Contact recipient to confirm fax machine is operational
  3. Retry sending during off-peak hours
  4. Check RingCentral delivery reports for detailed error codes


Changelog

VersionDateChanges
1.1.0December 17, 2025Added outbound fax sending — pharmacy faxes now sent through this service
1.0.0December 17, 2025Initial release — inbound fax processing extracted from CurentaOrderTriagingCoreApi

Owners

RoleContact
TeamPlatform Engineering
SupportSlack: #platform-engineering