FaxCore.Ev6.RestClient 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package FaxCore.Ev6.RestClient --version 2.0.0
                    
NuGet\Install-Package FaxCore.Ev6.RestClient -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FaxCore.Ev6.RestClient" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FaxCore.Ev6.RestClient" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="FaxCore.Ev6.RestClient" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FaxCore.Ev6.RestClient --version 2.0.0
                    
#r "nuget: FaxCore.Ev6.RestClient, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FaxCore.Ev6.RestClient@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FaxCore.Ev6.RestClient&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=FaxCore.Ev6.RestClient&version=2.0.0
                    
Install as a Cake Tool

FaxCore EV6 REST Client

A .NET client library for the FaxCore EV6 REST API. Handles OAuth authentication, HTTP communication, and serialization so you can focus on integrating fax capabilities into your application.

Supported Frameworks

Framework Versions
.NET Framework 4.7, 4.7.1, 4.7.2, 4.8
.NET 6.0, 7.0, 8.0

Installation

dotnet add package FaxCore.Ev6.RestClient

Or via the NuGet Package Manager:

Install-Package FaxCore.Ev6.RestClient

Quick Start

Prerequisites

You need a FaxCore EV6 server and a set of OAuth client credentials (client ID and client secret) configured on that server.

Direct Instantiation

FaxClient implements IDisposable — use a using block or dispose manually:

using FaxCore.Ev6.RestClient;

using (var client = new FaxClient("https://your-faxcore-server.com", "your-client-id", "your-client-secret"))
{
    var status = await client.GetMessageStatus(messageId);
}

Pass an externally managed HttpClient to avoid socket exhaustion in high-throughput scenarios. The provided HttpClient will not be disposed when the FaxClient is disposed:

using FaxCore.Ev6.RestClient;

// In Startup.cs / Program.cs
services.AddHttpClient("FaxCore");
services.AddSingleton<IFaxClientFactory, FaxClientFactory>();

services.AddScoped<IFaxClient>(provider =>
{
    var httpClientFactory = provider.GetRequiredService<IHttpClientFactory>();
    var httpClient = httpClientFactory.CreateClient("FaxCore");
    var factory = provider.GetRequiredService<IFaxClientFactory>();
    return factory.Create("https://your-faxcore-server.com", "your-client-id", "your-client-secret", httpClient);
});

Dependency Injection (Simple)

Register the client with your IoC container using the built-in factory:

using FaxCore.Ev6.RestClient;

services.AddSingleton<IFaxClientFactory, FaxClientFactory>();

services.AddSingleton<IFaxClient>(provider =>
{
    var factory = provider.GetRequiredService<IFaxClientFactory>();
    return factory.Create("https://your-faxcore-server.com", "your-client-id", "your-client-secret");
});

Then inject IFaxClient into your classes:

public class FaxService
{
    private readonly IFaxClient _faxClient;

    public FaxService(IFaxClient faxClient)
    {
        _faxClient = faxClient;
    }
}

Usage Examples

Sending a Fax

Sending a fax is a two-step process: first upload the document, then send the message with the upload response.

// Step 1: Upload the document
var uploadResult = await _faxClient.UploadFile("/path/to/document.pdf");
var uploadedFile = uploadResult.Data.First();

// Step 2: Build and send the message
var request = new SendMessageRequest<MessageRecipient>
{
    Message = new MessageRequest<MessageRecipient>
    {
        SenderName = "John Doe",
        Subject = "Invoice #1234",
        Note = "Please review the attached invoice.",
        Priority = 70,
        Recipients = new List<MessageRecipient>
        {
            new MessageRecipient
            {
                Address = "15551234567",
                Name = "Jane Smith",
                IsRawFax = true
            }
        },
        Documents = new List<Document>
        {
            new Document
            {
                Name = uploadedFile.Id,
                Path = uploadedFile.FileName,
                IsMerge = false
            }
        },
        Agents = new List<Agent>(),
        Trackings = new List<Tracking>()
    }
};

var sendResult = await _faxClient.SendMessage(request);
string messageId = sendResult.Data.MessageId;

Checking Message Status

var status = await _faxClient.GetMessageStatus(messageId);
// status.Data.Status returns a MessageStatus enum:
// Completed, Failed, Processing, InQueue, InSchedule, Approved, etc.

Getting Message Details

var details = await _faxClient.GetMessageDetails(messageId);
var message = details.Data;

Downloading a Fax

var fileBytes = await _faxClient.DownloadMessage(new DownloadMessageRequest
{
    MessageID = messageId,
    DeliveryNum = 1,
    DownloadType = FileType.PDF
});

// Write to file
File.WriteAllBytes("downloaded-fax.pdf", fileBytes);

Listing Message Folders

var folders = await _faxClient.GetMessageFolders();
foreach (var folder in folders.Data)
{
    Console.WriteLine(folder.FolderName);
}

Searching Messages

var results = await _faxClient.SearchMessages(new MessageSearchRequest
{
    // Set your search criteria
});
// results.Data.Records contains the matching messages
// results.Data.Pagination contains paging info

Managing Users

// Create a user
var newUser = await _faxClient.CreateUser(new CreateUserRequest { /* ... */ });

// Get user details
var user = await _faxClient.GetUserDetails(userId);

// Search users
var searchResults = await _faxClient.UserSearch(new UserSearchRequest { /* ... */ });

// List users in a domain
var users = await _faxClient.UserList("domain-name");

Managing Contacts

// Create an address book
var addressBook = await _faxClient.CreateAddressBook("My Contacts");

// Add a contact
var contact = await _faxClient.CreateContact(new CreateContactRequest { /* ... */ });

// List contacts
var contacts = await _faxClient.ListContacts(new ContactListRequest { /* ... */ });

// Update a contact
await _faxClient.UpdateContact(new UpdateContactRequest { /* ... */ });

// Get a single contact
var singleContact = await _faxClient.GetContact(contactId);

Managing Printers

// List printers
var printers = await _faxClient.GetPrinters();

// Create a printer
await _faxClient.CreatePrinter(new CreatePrinterRequest { /* ... */ });

// Delete printers
await _faxClient.DeletePrinters(new List<int> { printerId1, printerId2 });

Managing Inbound Routes

// Create a routing rule
await _faxClient.CreateRoutingRule(new RouteCreateRequest { /* ... */ });

// Search routes
var routes = await _faxClient.SearchRoutingRules("search-term");

// Enable/disable routes
await _faxClient.EnableRoutingRule(routeId);
await _faxClient.DisableRoutingRule(routeId);

API Reference

All methods are asynchronous and return Task<T>. API responses are wrapped in FaxCoreResponse<T> (with Status, Message, and Data properties) or PagedResponse<T> for paginated results.

Messages

Method Description
SendMessage(request) Send a fax to external recipients
SendMessageInternal(request) Send a fax to internal users
GetMessageStatus(messageId) Get the status of a message
GetMessageDetails(messageId) Get full message details
GetMessageFolders() List user's message folders
GetMessageList(request) List messages in a folder (paginated)
SearchMessages(request) Search messages (paginated)
DownloadMessage(request) Download a message as PDF or TIF (returns byte[])
RetrieveImage(messageId, seq, page, width) Retrieve a message image
MarkMessageAsDownloaded(messageId) Mark a message as downloaded
ToggleMessageRead(messageId, read) Mark a message as read/unread
GetMessageReadStatus(messageId) Get message read status
MoveMessageToFolder(messageId, folder) Move a message to another folder
ForwardMessageToUsers(messageId, users) Forward a message to users
UpdateMessageSubject(messageId, subject) Update message subject
UpdateMessageTracking(messageId, value, id) Update tracking info
GetMessageTracking(messageId) Get tracking info
RetryMessage(messageId) Retry a failed message
CancelMessage(messageId) Cancel an in-progress message
DeleteMessage(messageIds) Delete one or more messages
DeleteTrashMessages(messageIds) Delete messages from trash
DeleteMessageStatus(messageId) Get delete status of a message
ApproveMessage(messageId, approve, notes) Approve or reject a message
AssignMessage(messageId, ownerId, users) Assign a message to users
DelegateMessage(request) Send a message on behalf of another user

Files

Method Description
UploadFile(filePath) Upload a file (required before sending)
GetCoverPageItems() List available cover pages

Users

Method Description
CreateUser(request) Create a new user
GetUserDetails(userId) Get user details
GetUserFaxSettings(userId) Get user's fax settings
UpdateUser(request) Update user profile
UpdateUserConfig(request) Update user configuration
UpdateProfile(request) Update a user's profile by user ID
UserSearch(request) Search users (paginated)
UserList(domainName) List users in a domain
ActivateUsers(users) Activate users
DeactivateUser(users) Deactivate users
DeleteUser(userId) Delete a user
ChangeUserDomain(userId, domain) Move user to another domain

Contacts & Address Books

Method Description
CreateAddressBook(name) Create an address book
GetAddressBooks(pagination) List address books (paginated)
UpdateAddressBook(id, name) Update an address book name
DeleteAddressBook(id) Delete an address book
CreateContact(request) Create a contact
GetContact(id) Get a single contact by ID
ListContacts(request) List contacts (paginated)
UpdateContact(request) Update a contact
DeleteContact(id) Delete a contact

MFP

Method Description
GetMfpAddressBooks(request) Retrieve MFP address books
GetMfpContacts(request) Retrieve MFP contacts

Printers

Method Description
GetPrinters() List printers
CreatePrinter(request) Create a printer
DeletePrinters(printerIds) Delete one or more printers

Domains

Method Description
GetDomains() List accessible domains
CreateDomain(request) Create a new domain

Routes

Method Description
CreateRoutingRule(request) Create an inbound routing rule
SearchRoutingRules(criteria) Search routing rules
EnableRoutingRule(routeId) Enable a routing rule
DisableRoutingRule(routeId) Disable a routing rule
DeleteRoutingRule(routeId) Delete a routing rule

Cancellation and Timeout

All async methods accept an optional CancellationToken as the last parameter:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var status = await _faxClient.GetMessageStatus(messageId, cts.Token);

The default HTTP timeout is 1 minute. You can configure it via the constructor:

// 5-minute timeout for large file operations
var client = new FaxClient(url, clientId, clientSecret, null, TimeSpan.FromMinutes(5));

When providing an external HttpClient, the timeout parameter is ignored — configure the timeout on your HttpClient directly.

Resilience and Retry

This library does not include retry or circuit breaker policies by design. Different API operations have different idempotency characteristics (e.g., retrying SendMessage could double-send a fax), so resilience decisions are left to the consumer.

To add retry, circuit breaker, or other resilience policies, configure your HttpClient pipeline before passing it to FaxClient:

// Using Microsoft.Extensions.Http.Resilience or Polly
services.AddHttpClient("FaxCore")
    .AddStandardResilienceHandler();  // or .AddPolicyHandler(...)

// Then pass the configured HttpClient to FaxClient
var httpClient = httpClientFactory.CreateClient("FaxCore");
var client = new FaxClient(url, clientId, clientSecret, httpClient);

The library does perform a single automatic retry on transient server errors (5xx, network failures) during OAuth token refresh, since token acquisition is inherently idempotent.

Error Handling

The client throws FaxCoreException for API errors, which includes:

  • ErrorCode — the HTTP status code
  • HttpResponse — the raw response body from the server
  • Message — a description of the error
try
{
    var result = await _faxClient.GetMessageDetails("invalid-id");
}
catch (FaxCoreException ex)
{
    Console.WriteLine($"Error {ex.ErrorCode}: {ex.Message}");
    Console.WriteLine($"Response: {ex.HttpResponse}");
}

Authentication failures (invalid credentials, expired tokens) throw FaxCoreException with the message "Authentication failed" and the server's error response.

Parameter validation errors (missing required fields) also throw FaxCoreException with an inner ArgumentException.

Contributing

Prerequisites

  • .NET 8.0 SDK (required for building and running tests)
  • Access to a FaxCore EV6 server (required for running integration tests)

Building

dotnet build src/FaxCoreRestClient.sln

Running Tests

Tests are integration tests that require a live FaxCore server. Set the following environment variables before running:

export FAX_SERVER_URL="https://your-faxcore-server.com"
export FAX_SERVER_CLIENT="your-client-id"
export FAX_SERVER_SECRET="your-client-secret"

You can also create a local_settings.sh file in the project root and source it:

source local_settings.sh

Then run the tests:

# Run all tests
dotnet test src/FaxCore.Tests/FaxCore.Tests.csproj

# Run a specific test
dotnet test src/FaxCore.Tests/FaxCore.Tests.csproj --filter "FullyQualifiedName~FaxIsSubmittedToFaxServerSuccessfully"

Project Structure

src/
├── FaxCoreRestClient.sln                # Solution file
├── FaxCore.Ev6.RestClient/              # Main library (NuGet package)
│   ├── FaxClient.cs                     # Public client — constructor, file upload, cover pages
│   ├── MessageClient.cs                 # Message operations (partial class)
│   ├── UserClient.cs                    # User operations (partial class)
│   ├── ContactsService.cs              # Contact/address book operations (partial class)
│   ├── DomainsClient.cs                # Domain operations (partial class)
│   ├── RoutesClient.cs                 # Routing operations (partial class)
│   ├── PrinterClient.cs               # Printer operations (partial class)
│   ├── MfpClient.cs                   # MFP operations (partial class)
│   ├── Endpoints.cs                   # API endpoint path constants
│   ├── FaxCoreBaseClient.cs            # Internal HTTP client (auth, serialization)
│   ├── FaxClientFactory.cs             # DI factory
│   ├── IFaxClient.cs                   # Public interface (extends IDisposable)
│   ├── Models/
│   │   ├── Request/                    # Request DTOs
│   │   ├── Response/                   # Response DTOs
│   │   └── Enumerators/               # Enums (MessageStatus, FileType, Role, etc.)
│   └── Tools/                          # JSON converters and utilities
├── FaxCore.Functional.Core/            # Utility library (Option<T> monad, Config)
├── FaxCore.Tests/                      # xUnit integration tests
└── FaxCore.Sandbox/                    # Console app for manual testing

The FaxClient class is split across multiple files using C# partial classes, organized by API domain (messages, users, contacts, domains, routes, printers, MFP).

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net47 is compatible.  net471 is compatible.  net472 is compatible.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 232 3/25/2026
2.0.0 125 3/25/2026
1.0.3 269 1/31/2024
1.0.1 372 6/18/2023

v2.0.0 — Major release with breaking changes, bug fixes, and full API coverage.

BREAKING CHANGES:
- DownloadMessage now returns Task<byte[]> instead of Task<string> (binary content was being corrupted by UTF-8 string encoding)
- IFaxClient now extends IDisposable — call Dispose() or use 'using' blocks to release resources
- All endpoint paths aligned to the Swagger spec (plural paths like /api/users/ changed to /api/user/, HTTP methods corrected for Approve/Assign/Cancel)
- ApproveMessage, AssignMessage, CancelMessage changed from PUT to POST per API spec
- ChangeUserDomain changed from PUT /api/users/domain to POST /api/user/move per API spec

NEW FEATURES:
- Full API coverage: 11 new endpoints (Printers, MFP, DeleteTrash, RetrieveImage, GetContact, UpdateAddressBook, UpdateContact, UpdateProfile)
- CancellationToken support on all async methods (optional, non-breaking for callers)
- Configurable timeout via constructor parameter
- HttpClient injection — pass your own HttpClient for IHttpClientFactory integration
- Factory overloads for HttpClient and timeout

BUG FIXES:
- Fixed inverted token refresh logic (was re-fetching when valid, skipping when expired)
- Fixed Accept header corruption after DownloadFile (shared state mutation)
- Fixed authentication error handling (now throws clear FaxCoreException instead of NullReferenceException)
- Fixed timezone handling in token expiry comparison (DateTime.Now vs UTC)
- Fixed file upload double memory allocation (now streams from disk)
- Fixed null collection parameters throwing unguarded NullReferenceException
- Fixed double-slash URL construction
- Fixed base URL edge cases with multiple trailing slashes

IMPROVEMENTS:
- Thread-safe token management with SemaphoreSlim (double-check locking pattern)
- Per-request headers — eliminated shared mutable state (DefaultRequestHeaders)
- Token refresh with 30-second buffer to prevent TOCTOU races
- Single retry on transient failures during token fetch (5xx/network errors only)
- Centralized endpoint path constants (Endpoints.cs)
- Removed all Console.WriteLine debug output and HttpLoggingHandler credential logging
- NullToEmptyStringConverter.Read() no longer throws NotImplementedException