FaxCore.Ev6.RestClient
2.0.0
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
<PackageReference Include="FaxCore.Ev6.RestClient" Version="2.0.0" />
<PackageVersion Include="FaxCore.Ev6.RestClient" Version="2.0.0" />
<PackageReference Include="FaxCore.Ev6.RestClient" />
paket add FaxCore.Ev6.RestClient --version 2.0.0
#r "nuget: FaxCore.Ev6.RestClient, 2.0.0"
#:package FaxCore.Ev6.RestClient@2.0.0
#addin nuget:?package=FaxCore.Ev6.RestClient&version=2.0.0
#tool nuget:?package=FaxCore.Ev6.RestClient&version=2.0.0
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);
}
With IHttpClientFactory (Recommended for ASP.NET Core)
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 codeHttpResponse— the raw response body from the serverMessage— 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 | Versions 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. |
-
.NETFramework 4.7
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
.NETFramework 4.7.1
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
.NETFramework 4.7.2
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
.NETFramework 4.8
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
net6.0
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
net7.0
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
-
net8.0
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 7.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
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