TNZAPI.NET
TNZAPI.NET is TNZ's official .NET library for sending SMS, Email, TTS, Voice, Fax, WhatsApp, RCS, and Workflow messages, and for managing your Addressbook and OptOut list, distributed via NuGet. Drop it into your project and skip writing your own HTTP client, request signing, and response parsing against the TNZ REST API. Send your first message in a few lines of C#.
As of v3.00, this library wraps TNZ's JSON REST API (previously an XML API). If you're upgrading from an earlier version, see VERSIONS.md in the repository for the full migration history and TNZ's versioning strategy.
What You Can Do
Send & Receive Messages:
- Workflow: trigger multi-channel, no-code automation templates
- SMS: two-way text messaging
- Email: plain-text or HTML, with attachments
- TTS: text-to-speech voice calls
- Voice: pre-recorded audio calls
- Fax: document delivery
- WhatsApp: template messaging
- RCS: Rich Communication Services
Manage Your Account:
- Addressbook: contacts, groups, and relationships
- OptOut: manage opted-out destinations
- Webhooks: receive inbound delivery/reply events on your own server
Supported .NET Versions
This library supports the following .NET implementations:
- .NET Standard 2.0 (covers .NET Framework 4.6.1+, Mono, Xamarin, Unity, etc.)
- .NET6
- .NET7
- .NET8
- .NET9
- .NET10
Installation
Install the package from NuGet:
dotnet add package TNZAPI.NET
Alternatively, you can download the source code from GitHub and build it yourself. The repository also includes TNZAPI.NET.Samples, a console app covering Addressbook, Messaging, and Webhooks usage, and TNZAPI.NET.Demo, a full API and web demo project.
Continue to Getting Started to create your first TNZApiClient.
Getting Started
Getting started with the TNZ API couldn't be easier. Create a TNZApiClient and you're ready to go.
Register an Account
If you don't already have a TNZ account, sign up here before continuing.
API Credentials
TNZAPI.NET authenticates every request with a JWT Auth Token.
Export your Auth Token
- Login to the TNZ Dashboard
- Navigate to 'Users'
- Create a new user or select an existing one
- Enable API access (if it's not already enabled)
- Click on the 'API' tab
- Enable 'Auth Token' and create a new Auth Token
- Click the 'Copy' button to copy it to your clipboard
Pass it directly to the constructor:
var client = new TNZApiClient("[Your Auth Token]");
or via a TNZApiUser:
var apiUser = new TNZApiUser()
{
AuthToken = "[Your Auth Token]"
};
var client = new TNZApiClient(apiUser);
Refresh or invalidate your Auth Token
- Login to the TNZ Dashboard
- Navigate to 'Users'
- Click on your API user
- Click on the 'API' tab
- Click the refresh/recycle button in the Auth Token section
- Update your applications to use the new Auth Token
Refreshing invalidates the old token immediately. Any application still using it will start failing authentication until it's updated with the new one.
Every response implements IApiResult: check response.Result == Enums.ResultCode.Success before reading response fields; on failure, response.ErrorMessage is a List<string> of human-readable error messages. Enums.ResultCode has four values: Success, Failed, Unauthorized, RecordNotFound.
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
else
{
foreach (var error in response.ErrorMessage)
{
Console.WriteLine($"- Error={error}");
}
}
Common Response Enums
Every channel's Status(...) and Action (Reschedule/Abort/Resubmit/Pacing) results share these enums, referenced from each channel's own Response tables below rather than repeated in each one:
| Enum | Values | Used on |
|---|---|---|
Enums.JobStatus | Pending, Delayed, Completed, CreditHold, Unknown | The job-level JobStatus/Status field on every *StatusApiResult and *ActionApiResult. |
Enums.MessageStatus | Success, Failed, Pending | The per-recipient Status field on every *RecipientResult. |
Enums.RecipientChannelType | SMS, Email, Voice, Fax, WhatsApp, RCS, Unknown | The per-recipient Type field on every *RecipientResult. SMS recipients report as SMS; both Voice and TTS calls report as Voice (there's no separate TTS value; you already know which endpoint you called). |
The Destination Model
Destination is one shared class used by every messaging channel (SMS, Email, Fax, TTS, Voice, WhatsApp, RCS, and Workflow), mirroring the Destinations array shape from the REST API docs field-for-field. Every channel accepts every field below; which address field(s) actually matter depends on the channel (SMS/WhatsApp/RCS read ToNumber/MobilePhone, Fax reads FaxNumber, Email reads EmailAddress, TTS/Voice read MainPhone, and Workflow can read ToNumber, MainPhone, and EmailAddress all at once for omni-channel routing), as shown in each channel's own Destination Fields table.
| Field | Type | Description |
|---|---|---|
Recipient | string? | Generic single-value shorthand; the server infers which address type it is from the channel you're sending on. Set by the single-string constructor and named-argument constructor's recipient parameter. |
ToNumber | string? | Phone destination, read by SMS, WhatsApp, and RCS. Also the destination for Workflow's toNumber shortcut. |
MobilePhone | string? | Alternative phone destination field alongside ToNumber. |
MainPhone | string? | Read by TTS and Voice. Also the destination for Workflow's mainPhone shortcut, a separate wire field from ToNumber. |
EmailAddress | string? | Read by Email. Can also be set on Workflow, alongside ToNumber/MainPhone, for omni-channel Workflow Templates. |
FaxNumber | string? | Read by Fax. |
Company | string? | Personalisation token [[Company]]. |
Attention | string? | Personalisation token [[Attention]]. |
FirstName | string? | Personalisation token [[FirstName]]. |
LastName | string? | Personalisation token [[LastName]]. |
Custom1–Custom9 | string? | Personalisation tokens [[Custom1]]–[[Custom9]]. |
ContactID | ContactID? | Send to this addressbook contact instead of a raw destination. |
GroupID | GroupID? | Send to every member of this addressbook group. |
GroupCode | string? | Alternative group lookup by code. Object-initializer only; no constructor overload sets it. |
Object initializer, with full personalisation
Set any combination of fields at once with new Destination { ... }, including a specific address field like ToNumber directly instead of the generic Recipient shorthand. This is also the only way to set GroupCode, since no constructor overload exposes it.
var destination = new Destination
{
ToNumber = "+64211111111",
Company = "Example Company",
Attention = "Accounts Payable",
FirstName = "Alice",
LastName = "Smith",
Custom1 = "Invoice #1234",
Custom2 = "Due 2026-08-01",
Custom3 = "VIP"
};
Named-argument constructor
Destination also has a constructor that takes companyName, attention, firstName, lastName, and custom1–custom9 as optional named parameters, useful for a compact one-line call. Its recipient parameter sets only the generic Recipient field, not ToNumber/MobilePhone/MainPhone/FaxNumber/EmailAddress individually; use the object initializer above if you need a specific address field set by name. No addressbook fields (ContactID/GroupID/GroupCode) are settable through this constructor either.
var destination = new Destination(
recipient: "+64211111111",
companyName: "Example Company",
firstName: "Alice",
custom1: "Invoice #1234",
custom3: "VIP"
);
Environment Variables
TNZAPI.NET can also be configured via environment variables (useful for CI, containers, or keeping credentials out of source control):
| Variable | Purpose | Default |
|---|---|---|
TNZ_AUTH_TOKEN | Fallback Auth Token used whenever a TNZApiUser is constructed without one set explicitly. An explicit AuthToken always takes precedence. | (none) |
TNZ_API_URL | Overrides the API host TNZAPI.NET sends requests to. Read fresh on every request, so it can be changed at runtime (e.g. between test cases). | https://api.tnz.co.nz |
TNZ_ALLOW_INSECURE_HTTP | Set to true to allow requests over plain HTTP (useful when pointing TNZ_API_URL at a local/staging server without TLS). By default TNZAPI.NET refuses to send the Auth Token over anything but HTTPS. | (unset, HTTPS enforced) |
// Picks up TNZ_AUTH_TOKEN automatically since no AuthToken is set explicitly
var client = new TNZApiClient();
Setting environment variables
Windows
Current PowerShell session only:
$env:TNZ_AUTH_TOKEN = "your-auth-token"
Current Command Prompt (cmd.exe) session only:
set TNZ_AUTH_TOKEN=your-auth-token
Permanently, for your user account (visible in new terminals/processes, including Visual Studio, after you open one):
[System.Environment]::SetEnvironmentVariable("TNZ_AUTH_TOKEN", "your-auth-token", "User")
Or via the GUI: Windows Settings → search "Environment Variables" → Edit environment variables for your account.
Linux
Current shell session only:
export TNZ_AUTH_TOKEN="your-auth-token"
Permanently, for your user account: add the export line above to ~/.bashrc (bash), ~/.zshrc (zsh), or ~/.profile, then start a new shell (or source the file).
For a systemd service, set it in the unit file instead:
[Service]
Environment="TNZ_AUTH_TOKEN=your-auth-token"
macOS
Current shell session only:
export TNZ_AUTH_TOKEN="your-auth-token"
Permanently, for your user account: add the export line above to ~/.zshrc (the default shell on modern macOS) or ~/.bash_profile, then start a new terminal (or source the file).
Same pattern applies to TNZ_API_URL and TNZ_ALLOW_INSECURE_HTTP. Just swap the variable name. Note that TNZAPI.NET reads OS-level environment variables only; it does not auto-load a .env file.
Actions Support by Channel
client.Actions is per-messaging-channel, not a single flat facade: each channel exposes only the actions its real API supports. Each channel's own section below shows its supported action(s) with a code sample.
| Channel | Reschedule | Abort | Resubmit | Pacing |
|---|---|---|---|---|
| SMS | ✓ | ✓ | ||
| ✓ | ✓ | ✓ | ||
| TTS | ✓ | ✓ | ✓ | ✓ |
| Voice | ✓ | ✓ | ✓ | ✓ |
| Fax | ✓ | ✓ | ✓ | |
| ✓ | ✓ | |||
| RCS | ✓ | ✓ | ||
| Workflow |
Workflow
Workflow triggers a pre-configured messaging strategy with a single API call. Build the strategy once in the Dashboard's drag-and-drop builder, then trigger it from your application using this library, with no code changes needed when the strategy itself changes. A Workflow Template can chain channels together: start with an SMS, fail over to an Email if there's no reply, then trigger a TTS call if the email goes unread. It can define fallback channels so a message keeps trying until it gets through, and fire custom webhooks at any stage.
Workflow is the only messaging module with no Message/TemplateID text content, no Status, no Received, and no client.Actions.Workflow, just Send.
Quick Example
Workflow is genuinely omni-channel: unlike every other module, toNumber, mainPhone, and emailAddress can all be set together for the same destination. The Workflow Template decides which channel(s) actually get used.
var response = client.Messaging.Workflow.SendMessage(
workflowTemplateId: "a1b2c3d4-e5f6-7890-1234-567890abcdef",
toNumber: "+64211111111",
emailAddress: "test@example.com",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (WorkflowModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
WorkflowTemplateID | string? | Yes | ID of the Workflow Template to trigger (built in the Dashboard). |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to. The builder and the named-parameter shortcut fold this into Destinations for you; if you construct WorkflowModel directly, setting only ContactID with an empty Destinations fails client-side validation, so populate Destinations too. |
GroupID | GroupID? | No | Single addressbook group to send to. Same caveat as ContactID above. |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
ChargeCode | string? | No | Billing charge code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/mainPhone/emailAddress/contactID/groupID/contactIDs/groupIDs/destination parameters, or via WorkflowBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.Workflow.SendMessage(...) also accepts workflowTemplateId, destination, toNumber, mainPhone, emailAddress, contactID, groupID, contactIDs, groupIDs, destinations, notificationType, sendMode directly. This covers nearly the entire model, since Workflow has no message-content or attachment fields. For SendTime, SubAccount, etc. use WorkflowBuilder or construct a WorkflowModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. Workflow is the one channel where ToNumber, MainPhone, and EmailAddress can all be set on the same Destination at once, letting one Workflow Template route to whichever channel(s) it's configured for.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand; the server infers which address type it is from the channel(s) the Workflow Template routes to. |
ToNumber | Phone destination, e.g. "+64211111111". Sent as the toNumber shortcut's destination. Can be set alongside MainPhone and EmailAddress on the same Destination for omni-channel Workflow Templates. |
MobilePhone | Alternative phone destination field alongside ToNumber. |
MainPhone | Phone destination sent as the mainPhone shortcut's destination: a separate wire field from ToNumber, letting a Workflow Template distinguish the two. Can be set alongside ToNumber and EmailAddress for omni-channel routing. |
EmailAddress | Email destination. Can be set alongside ToNumber/MainPhone on the same Destination for omni-channel Workflow Templates. |
FaxNumber | Fax destination, read if the Workflow Template routes to a Fax channel. |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Personalisation tokens [[Custom1]]–[[Custom9]], passed through to whichever channel(s) the Workflow Template actually uses. |
ContactID | Addressbook contact reference. Sends to that contact instead of raw addresses. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination, multi-channel shorthand
Trigger a Workflow Template for one destination, setting multiple channel addresses at once. The Template itself decides which channel(s) actually get used.
var response = client.Messaging.Workflow.SendMessage(
workflowTemplateId: "a1b2c3d4-e5f6-7890-1234-567890abcdef",
toNumber: "+64211111111",
emailAddress: "test@example.com",
sendMode: Enums.SendModeType.Test
);
Via the builder
Use WorkflowBuilder when you need more control than the named-parameter shortcut, e.g. scheduling.
using var builder = new WorkflowBuilder();
var model = builder
.SetWorkflowTemplateID("a1b2c3d4-e5f6-7890-1234-567890abcdef")
.AddDestination("+64211111111")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Workflow.SendMessage(model);
Destination with every channel address set
Build a Destination with ToNumber, MainPhone, and EmailAddress all set at once, plus personalisation fields. This is the pattern that makes Workflow genuinely omni-channel: the Template picks whichever destination(s) it needs from the same Destination.
using var builder = new WorkflowBuilder();
var destination = new Destination
{
ToNumber = "+64211111111",
MainPhone = "+6491112222",
EmailAddress = "test@example.com",
FirstName = "Alice",
Custom1 = "Account #4432"
};
var model = builder
.SetWorkflowTemplateID("a1b2c3d4-e5f6-7890-1234-567890abcdef")
.AddDestination(destination)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Workflow.SendMessage(model);
Multiple destinations
Trigger the same Workflow Template for more than one destination in a single request.
using var builder = new WorkflowBuilder();
var model = builder
.SetWorkflowTemplateID("a1b2c3d4-e5f6-7890-1234-567890abcdef")
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Workflow.SendMessage(model);
Scheduled send
Delay the trigger to a specific SendTime.
using var builder = new WorkflowBuilder();
var model = builder
.SetWorkflowTemplateID("a1b2c3d4-e5f6-7890-1234-567890abcdef")
.AddDestination("+64211111111")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Workflow.SendMessage(model);
Response
Workflow has no Status, Received, or Action methods. The Dto folder for this channel contains only one response type.
SendMessage(...) → WorkflowApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the Workflow run you just triggered. |
SMS
Send text messages to one or more destinations via the TNZ REST API, with optional Voice/RCS/WhatsApp fallback. SMS is more than a one-way notification channel. It's a complete two-way messaging solution: track delivery status in real time (see Poll for Status below), and let recipients reply to keep the conversation going (see Poll for Inbound SMS below, or the Webhooks section for a push-based alternative).
Message Body Tokens
Beyond the personalisation tokens in the Destination Fields table below, your Message body supports these special inline tokens:
[[Link:https://example.com/page]]: automatically shortens the URL and tracks click-through engagement (URL Shortener).[[File1]]: inserts a link to the first file attached via theFilesparameter ([[File2]],[[File3]], etc. for additional attachments) (File Link).[[REPLY]]: inserts a tappable link recipients can use to reply, even from devices without native SMS reply support (Reply Link).[[STOP]]: inserts an unsubscribe link that automatically opts the recipient out of future messages (Unsubscribe).
var response = client.Messaging.SMS.SendMessage(
toNumber: "+64211111111",
messageText: "View your invoice at [[Link:https://example.com/invoice/123]] or reply [[REPLY]]. Text [[STOP]] to opt out.",
sendMode: Enums.SendModeType.Test
);
Quick Example
var response = client.Messaging.SMS.SendMessage(
toNumber: "+64211111111",
messageText: "Test SMS",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (SMSModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
Message | string? | Yes* | Message body. Supports personalisation tokens [[FirstName]], [[Custom1]], etc. |
TemplateID | string? | Yes* | Pre-configured message template ID (alternative to Message). |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
FromNumber | string? | No | Sender ID shown on the recipient's device. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime (e.g. "New Zealand", "AUS Eastern"). |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
FallbackMode | ICollection<Enums.SMSFallbackMode>? | No | Fallback channel(s) if SMS fails, tried in the order given, e.g. RCS then Voice. |
SMSEmailReply | string? | No | Email address to receive SMS replies. |
CharacterConversion | bool | No | Convert characters outside the GSM character set automatically. Default false. |
Files | ICollection<Attachment>? | No | Files sent via MessageLink. NZ carriers don't support true MMS; reference the file in your message text with [[File1]] instead, and the recipient gets a link to it. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*Either Message or TemplateID must be provided.
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via SMSBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.SMS.SendMessage(...) also accepts a subset of these directly: messageText, reference, destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, file, attachments, notificationType, sendMode. For every other field (SendTime, Timezone, SubAccount, FallbackMode, etc.) use SMSBuilder or construct an SMSModel directly. destination accepts either a plain string or a Destination object directly (useful for a single personalised send without the builder); anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. SMS reads ToNumber/MobilePhone as the destination.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand. SMS treats this as the destination phone number when no more specific field is set. Set by the single-string constructor. |
ToNumber | Destination phone number, e.g. "+64211111111". Read by SMS. |
MobilePhone | Alternative phone destination field, also read by SMS. |
MainPhone | Accepted but not read by SMS (used by TTS/Voice). |
EmailAddress | Accepted but not read by SMS (used by Email). |
FaxNumber | Accepted but not read by SMS (used by Fax). |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Arbitrary per-destination personalisation values, [[Custom1]] … [[Custom9]]. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
new Destination("+64211111111") sets only the generic Recipient field; the server infers it's a phone number from the SMS channel context. Use the object initializer (new Destination { ToNumber = "...", FirstName = "..." }) when you need a specific address field or personalisation set explicitly.
Code Samples
Single destination shorthand
The simplest way to send: pass a message and destination directly to SendMessage(...).
var response = client.Messaging.SMS.SendMessage(
toNumber: "+64211111111",
messageText: "Office closed today.",
sendMode: Enums.SendModeType.Test
);
Multiple destinations, via the builder
Use SMSBuilder when you need multiple destinations, or want to mix raw numbers with existing Addressbook contacts and groups.
var groupID = new GroupID("GGGGGGGG-BBBB-BBBB-CCCC-DDDDDDDDDDDD");
var contactID = new ContactID("CCCCCCCC-BBBB-BBBB-CCCC-DDDDDDDDDDDD");
using var builder = new SMSBuilder();
var model = builder
.SetMessageText("Test SMS")
.SetReference("Test SMS - Builder sample")
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.AddDestination(contactID) // Destination from TNZ Addressbook by ContactID
.AddDestinations(groupID) // Destinations from TNZ Addressbook by GroupID
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.SMS.SendMessage(model);
Bulk send to multiple addressbook groups and contacts
Pass lists of existing Addressbook groups and contacts directly to the named-parameter SendMessage(...) shortcut's groupIDs/contactIDs parameters to message everyone in them in one call.
var groupIDs = new List<GroupID>
{
new GroupID("GGGGGGGG-BBBB-BBBB-CCCC-DDDDDDDDDDDD"),
new GroupID("GGGGGGGG-BBBB-BBBB-CCCC-EEEEEEEEEEEE")
};
var contactIDs = new List<ContactID>
{
new ContactID("CCCCCCCC-BBBB-BBBB-CCCC-DDDDDDDDDDDD"),
new ContactID("CCCCCCCC-BBBB-BBBB-CCCC-EEEEEEEEEEEE")
};
var response = client.Messaging.SMS.SendMessage(
messageText: "Reminder: your subscription renews tomorrow.",
groupIDs: groupIDs,
contactIDs: contactIDs,
sendMode: Enums.SendModeType.Test
);
Bulk send with per-destination personalisation
Send one message to many destinations while personalising each copy with Destination object initializers instead of plain strings.
using var builder = new SMSBuilder();
var model = builder
.SetMessageText("Hi [[FirstName]], your appointment is on [[Custom1]].")
.AddDestination(new Destination
{
ToNumber = "+64211111111",
FirstName = "Alice",
Custom1 = "Monday 3pm"
})
.AddDestination(new Destination
{
ToNumber = "+64222222222",
FirstName = "Bob",
Custom1 = "Tuesday 10am"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.SMS.SendMessage(model);
Send a file via MessageLink
NZ carriers don't support MMS. Attach a file with SMSBuilder.AddAttachment(...) and reference it in the message text with [[File1]]; the recipient gets an SMS with a link to the file instead of an inline attachment.
using var builder = new SMSBuilder();
var model = builder
.SetMessageText("Here's the photo you requested: [[File1]]")
.AddDestination("+64211111111")
.AddAttachment(@"C:\files\photo.jpg") // reads and base64-encodes the file
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.SMS.SendMessage(model);
Scheduled send with webhook callback
Combine SendTime/Timezone to delay delivery with WebhookCallbackURL to get notified the moment it completes, instead of polling Status(...).
using var builder = new SMSBuilder();
var model = builder
.SetMessageText("Your reminder.")
.AddDestination("+64211111111")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetWebhookCallbackURL("https://yourapp.example.com/webhooks/sms")
.SetWebhookCallbackFormat(Enums.WebhookCallbackType.JSON)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.SMS.SendMessage(model);
Poll for status
Check delivery progress and per-recipient results any time after sending.
var status = client.Messaging.SMS.Status(response.MessageID);
if (status.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"JobStatus: {status.JobStatus}");
foreach (var recipient in status.Recipients)
{
Console.WriteLine($" -> {recipient.Destination}: {recipient.Status} ({recipient.Result})");
foreach (var reply in recipient.SMSReplies)
{
Console.WriteLine($" reply: {reply.MessageText}");
}
}
}
Poll for inbound SMS
Retrieve SMS replies received in the last timePeriod minutes, as an alternative to configuring a webhook.
var received = client.Messaging.SMS.Received(timePeriod: 1440); // minutes
if (received.Result == Enums.ResultCode.Success)
{
foreach (var message in received.Messages)
{
Console.WriteLine($"From {message.From}: {message.MessageText}");
}
}
Poll for replies to a specific message
Retrieve replies to one outbound MessageID, as an alternative to scanning all inbound SMS with Received(...).
var replies = client.Messaging.SMS.SMSReply(response.MessageID);
if (replies.Result == Enums.ResultCode.Success)
{
foreach (var recipient in replies.Recipients)
{
foreach (var reply in recipient.SMSReplies)
{
Console.WriteLine($"{recipient.Destination} replied: {reply.MessageText}");
}
}
}
Actions
SMS supports Reschedule and Abort (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-sent message to a new sendTime.
var response = client.Actions.SMS.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Action: {response.Action}, Status: {response.Status}");
}
Abort
Cancel a scheduled message before it sends.
client.Actions.SMS.Abort(response.MessageID);
Response
SendMessage(...) → SMSApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the message you just sent. |
Status(...) → SMSStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The message this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Recipients successfully delivered / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<SMSRecipientResult> | Per-recipient results. See table below. |
SMSRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's phone number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable delivery result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the message was actually sent to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this delivery, if available. |
Price | decimal? | Per-recipient cost. |
SMSReplies | List<SMSReplyResult> | Inbound replies from this recipient. See table below. |
SMSReplyResult (each entry in SMSReplies)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this reply. |
ReceivedTimeLocal / ReceivedTimeUTC | DateTime? | When the reply was received. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
From | string? | The replying phone number. |
MessageText | string? | The reply body. |
SMSReply(...) → SMSStatusApiResult
Same result type as Status(...) above, scoped to replies received against the given MessageID. Reply text is in each entry's Recipients[].SMSReplies[].MessageText. TotalRecords/RecordsPerPage/PageCount/Page paginate the Recipients list, controlled by SMSReply(...)'s recordsPerPage/page parameters.
Received(...) → SMSReceivedApiResult
| Field | Type | Description |
|---|---|---|
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Messages. |
Messages | List<SMSReceivedMessage> | Inbound SMS messages. See table below. |
SMSReceivedMessage (each entry in Messages)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this message. |
MessageID | MessageID? | The original outbound message this replies to, if determinable. |
JobNum | string? | The original send job's number, if applicable. |
SubAccount / Department | string? | Echoed billing codes from the original send. |
ReceivedTimeLocal / ReceivedTimeUTC / ReceivedTimeUTC_RFC3339 | DateTime? | When the message was received, in three formats. |
From | string? | The sender's phone number. |
ContactID | ContactID? | Addressbook contact reference, if the sender matched one. |
MessageText | string? | The message body. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
Version | string? | Payload format version. |
Reschedule(...)/Abort(...) → SMSActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The message this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Reschedule". |
Go beyond plain text with TNZ's flexible Email API. Send beautifully crafted HTML emails using your own content, or specify a TemplateID and let your team manage their own HTML designs using the Dashboard's WYSIWYG editor, with no code changes needed when the design updates. Attach files, and TNZ automatically tracks link clicks in the message body. Whether you're sending invoices, newsletters, or critical alerts, monitor delivery status every step of the way.
Quick Example
var response = client.Messaging.Email.SendMessage(
fromEmail: "from@test.com", // Optional - leave blank to use your API username as sender
emailSubject: "Test Email",
messagePlain: "Test Email Body",
emailAddress: "email.one@test.com",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (EmailModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
MessagePlain | string? | Yes* | Plain-text body. |
MessageHTML | string? | Yes* | HTML body. |
TemplateID | string? | Yes* | Pre-configured message template ID (alternative to MessagePlain/MessageHTML). |
EmailSubject | string? | Yes | Email subject line. |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
FromEmail | string? | No | Sender address. Leave blank to use your API username. |
From | string? | No | Sender display name. |
SMTPFrom | string? | No | SMTP envelope-from override. |
CCEmail | string? | No | CC address. |
ReplyTo | string? | No | Reply-To address. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
Files | ICollection<Attachment>? | No | Email attachments. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*Either MessagePlain, MessageHTML, or TemplateID must be provided; HTML and plain-text may be combined for multi-part email.
†Set directly via Destinations, via the named SendMessage(...) shortcut's emailAddress/contactID/groupID/contactIDs/groupIDs/destination parameters, or via EmailBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.Email.SendMessage(...) also accepts messagePlain, messageHTML, destination, emailAddress, contactID, groupID, contactIDs, groupIDs, destinations, emailSubject, fromEmail, file, attachments, notificationType, sendMode directly. For CCEmail, ReplyTo, SendTime, SubAccount, etc. use EmailBuilder or construct an EmailModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. Email reads EmailAddress as the destination.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand; sets the generic Recipient field, not EmailAddress directly. |
ToNumber | Accepted but not read by Email. |
MobilePhone | Accepted but not read by Email. |
MainPhone | Accepted but not read by Email. |
EmailAddress | Destination email address, read by Email as the destination, e.g. "email.one@test.com". |
FaxNumber | Accepted but not read by Email. |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Arbitrary per-destination personalisation values, [[Custom1]] … [[Custom9]]. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw address. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination, plain text
The simplest way to send: pass a plain-text body and destination directly to SendMessage(...).
var response = client.Messaging.Email.SendMessage(
fromEmail: "from@test.com",
emailSubject: "Test Email",
messagePlain: "Test Email Body",
emailAddress: "email.one@test.com",
sendMode: Enums.SendModeType.Test
);
Via the builder
Use EmailBuilder when you need fields the named-parameter shortcut doesn't cover, e.g. CCEmail, ReplyTo, or scheduling.
using var builder = new EmailBuilder();
var model = builder
.SetEmailSubject("Test Email")
.SetMessagePlain("Test Email Body")
.AddDestination("email.one@test.com")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Email.SendMessage(model);
HTML email
Email clients have inconsistent CSS support, so inline styles (rather than a <style> block) are the safest way to style an HTML body:
using var builder = new EmailBuilder();
var htmlBody = @"
<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;'>
<h2 style='color: #1057fc;'>Your Order Has Shipped!</h2>
<p>Hi [[FirstName]],</p>
<p>Great news! Your order <strong>#12345</strong> is on its way and should arrive within 2-3 business days.</p>
<p style='text-align: center; margin: 24px 0;'>
<a href='https://example.com/track/12345' style='background-color: #1057fc; color: #ffffff; padding: 12px 24px; border-radius: 4px; text-decoration: none;'>Track Your Order</a>
</p>
<p style='color: #787878; font-size: 12px;'>If you have any questions, reply to this email and we'll be happy to help.</p>
</div>";
var model = builder
.SetEmailSubject("Your order has shipped")
.SetMessageHTML(htmlBody)
.AddDestination(new Destination
{
EmailAddress = "email.one@test.com",
FirstName = "Alice"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Email.SendMessage(model);
With an attachment
Attach a file using its base64-encoded content.
using var builder = new EmailBuilder();
var model = builder
.SetEmailSubject("Test Email")
.SetMessagePlain("See attached.")
.AddDestination("email.one@test.com")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Email.SendMessage(model);
Custom sender, reply-to, and CC
Override the sender identity and add a CC and Reply-To address using the builder.
using var builder = new EmailBuilder();
var model = builder
.SetEmailSubject("Your invoice is ready")
.SetMessagePlain("Please find your invoice attached.")
.SetSMTPFrom("billing@test.com")
.SetFrom("Test Company Billing")
.SetFromEmail("billing@test.com")
.SetReplyTo("accounts@test.com")
.SetCCEmail("manager@test.com")
.AddDestination("email.one@test.com")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Email.SendMessage(model);
Bulk send to multiple addressbook groups and contacts
Pass lists of existing Addressbook groups and contacts directly to the named-parameter SendMessage(...) shortcut's groupIDs/contactIDs parameters to message everyone in them in one call.
var groupIDs = new List<GroupID>
{
new GroupID("GGGGGGGG-BBBB-BBBB-CCCC-DDDDDDDDDDDD"),
new GroupID("GGGGGGGG-BBBB-BBBB-CCCC-EEEEEEEEEEEE")
};
var contactIDs = new List<ContactID>
{
new ContactID("CCCCCCCC-BBBB-BBBB-CCCC-DDDDDDDDDDDD"),
new ContactID("CCCCCCCC-BBBB-BBBB-CCCC-EEEEEEEEEEEE")
};
var response = client.Messaging.Email.SendMessage(
emailSubject: "Test Email",
messagePlain: "Test Email Body",
groupIDs: groupIDs,
contactIDs: contactIDs,
sendMode: Enums.SendModeType.Test
);
Poll for status
Check delivery progress and per-recipient results any time after sending.
var status = client.Messaging.Email.Status(response.MessageID);
if (status.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"JobStatus: '{status.JobStatus}', JobNum: '{status.JobNum}'");
foreach (var recipient in status.Recipients)
{
Console.WriteLine($" -> {recipient.Destination}: {recipient.Status} ({recipient.Result})");
}
}
Actions
Email supports Reschedule, Abort, and Resubmit (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-sent email to a new sendTime.
var response = client.Actions.Email.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Action: {response.Action}, Status: {response.Status}");
}
Abort
Cancel a scheduled email before it sends.
client.Actions.Email.Abort(response.MessageID);
Resubmit
Resubmit a message for delivery at a new scheduled time, without rebuilding the whole request.
client.Actions.Email.Resubmit(response.MessageID, DateTime.Now.AddHours(1));
Response
SendMessage(...) → EmailApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the message you just sent. |
Status(...) → EmailStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The message this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Recipients successfully delivered / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<EmailRecipientResult> | Per-recipient results. See table below. |
EmailRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's email address. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable delivery result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the message was actually sent to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this delivery, if available. |
Price | decimal? | Per-recipient cost. |
Reschedule(...)/Abort(...)/Resubmit(...) → EmailActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The message this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Resubmit". |
TTS (Text-to-Speech)
Transform written text into clear, natural-sounding voice calls. Ideal for automated alerts, appointment reminders, or customer surveys. Route callers or capture responses with interactive keypad menus, and rely on built-in answering machine detection (AnswerPhoneMode) to deliver an alternate message when a voicemail picks up instead of a person.
Quick Example
var response = client.Messaging.TTS.SendMessage(
messageToPeople: "Hello, this is a call from test. This is relevant information.",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (TTSModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
MessageToPeople | string? | Yes* | Text read aloud to a human answering the call. |
TemplateID | string? | Yes* | Pre-configured message template ID (alternative to MessageToPeople). |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
MessageToAnswerPhones | string? | No | Alternative text read when an answering machine picks up. |
AnswerPhoneMode | Enums.AnswerPhoneMode? | No | How to handle an answering machine. |
Keypads | ICollection<KeypadModel>? | No | Keypad menu options. See Keypad fields below. |
KeypadOptionRequired | bool | No | Force the caller to press a key before the call proceeds. Default false. |
CallRouteMessageOnWrongKey | string? | No | Message played if an invalid key is pressed. |
EndCallMessage | string? | No | Message played just before the call ends. |
CallRouteMessageToPeople | string? | No | Message played before routing to an operator. |
CallRouteMessageToOperators | string? | No | Message played to the operator receiving the routed call. |
NumberOfOperators | int? | No | Number of simultaneous operators for keypad-routed calls. |
RetryAttempts | int? | No | Number of retry attempts on no-answer/busy. |
RetryPeriod | int? | No | Minutes between retry attempts. |
CallerID | string? | No | Caller ID shown to the recipient. |
Voice | string? | No | TTS voice to use. |
Options | string? | No | Additional provider-specific options. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
ChargeCode | string? | No | Billing charge code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*Either MessageToPeople or TemplateID must be provided.
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via TTSBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.TTS.SendMessage(...) also accepts messageToPeople, destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, notificationType, sendMode directly. TTS has no SendMessage(...) shortcut for keypads, attachments, or any other field above. Use TTSBuilder or construct a TTSModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Keypad Fields (KeypadModel)
| Field | Type | Description |
|---|---|---|
Tone | int | The DTMF digit this entry responds to. |
Play | string? | Message played when this key is pressed. |
RouteNumber | string? | Phone number to route the call to when this key is pressed. |
PlaySection | Enums.KeypadPlaySection? | Where in the call flow this keypad applies (e.g. Main). |
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. TTS reads MainPhone for its destination; other address fields (ToNumber, MobilePhone, EmailAddress, FaxNumber) are accepted but not read by TTS.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand; the server infers which address type it is from the channel you're sending on. |
ToNumber | Accepted but not read by TTS. |
MobilePhone | Accepted but not read by TTS. |
MainPhone | Destination phone number, e.g. "+64211111111". TTS reads MainPhone for its destination. |
EmailAddress | Accepted but not read by TTS. |
FaxNumber | Accepted but not read by TTS. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Arbitrary per-destination personalisation values, [[Custom1]] … [[Custom9]]. |
Code Samples
Single destination shorthand
The simplest way to send: pass the spoken text and destination directly to SendMessage(...).
var response = client.Messaging.TTS.SendMessage(
messageToPeople: "Hello, this is a call from test. This is relevant information.",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
With a keypad menu, via the builder
Add interactive keypad options so callers can route themselves to a different number by pressing a key.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Press 1 for sales, press 2 for support.")
.AddDestination("+64211111111")
.AddKeypad(new KeypadModel
{
Tone = 1,
Play = "Connecting you to sales now.",
PlaySection = Enums.KeypadPlaySection.Main,
RouteNumber = "+64211112222"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
Multi-option keypad menu
Offer several keys at once: route two of them to different numbers, play a short message on another without routing, and replay the original message on a fourth.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Press 1 for sales, press 2 for support, press 3 to hear our address, or press 9 to hear this message again.")
.AddDestination("+64211111111")
.AddKeypad(new KeypadModel
{
Tone = 1,
RouteNumber = "+64211112222"
})
.AddKeypad(new KeypadModel
{
Tone = 2,
RouteNumber = "+64211113333"
})
.AddKeypad(new KeypadModel
{
Tone = 3,
Play = "We're located at 123 Example Street, Auckland."
})
.AddKeypad(new KeypadModel
{
Tone = 9,
PlaySection = Enums.KeypadPlaySection.Main
})
.SetKeypadOptionRequired(true)
.SetCallRouteMessageOnWrongKey("Sorry, that key isn't recognised. Please try again.")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
Multiple destinations
Call more than one number in a single request using TTSBuilder.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Hello, this is a call from test.")
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
With personalisation
Build a Destination with MainPhone and personalisation fields set, for use with [[FirstName]]-style tokens in MessageToPeople.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Hello [[FirstName]], this is a reminder about your appointment on [[Custom1]].")
.AddDestination(new Destination
{
MainPhone = "+64211111111",
FirstName = "Alice",
Custom1 = "Monday 3pm"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
Scheduled send
Delay the call to a specific SendTime.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Your reminder call.")
.AddDestination("+64211111111")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
Retry attempts, caller ID, and reporting
Configure automatic retries on no-answer or busy, set the caller ID shown to recipients, choose a voice, and email a report once the job completes.
using var builder = new TTSBuilder();
var model = builder
.SetMessageToPeople("Hello, this is a call from test. This is relevant information.")
.AddDestination("+64211111111")
.SetCallerID("+6499999999")
.SetVoice("Female1")
.SetNumberOfOperators(1)
.SetRetryAttempts(3)
.SetRetryPeriod(1)
.SetReportTo("report@example.com")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.TTS.SendMessage(model);
Poll for status
Check call progress and per-recipient results any time after sending.
var status = client.Messaging.TTS.Status(response.MessageID);
if (status.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"JobStatus: '{status.JobStatus}', JobNum: '{status.JobNum}'");
foreach (var recipient in status.Recipients)
{
Console.WriteLine($" -> {recipient.Destination}: {recipient.Status} ({recipient.Result})");
}
}
Actions
TTS supports Reschedule, Abort, Resubmit, and Pacing (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-placed call to a new sendTime.
var response = client.Actions.TTS.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
Abort
Cancel a scheduled call before it's placed.
client.Actions.TTS.Abort(response.MessageID);
Resubmit
Re-send a call at a new scheduled time without rebuilding the whole request.
client.Actions.TTS.Resubmit(response.MessageID, DateTime.Now.AddHours(1));
Pacing (adjust simultaneous operators)
Change how many calls run at once on an in-progress keypad-routed job.
client.Actions.TTS.Pacing(response.MessageID, numberOfOperators: 1);
Response
SendMessage(...) → TTSApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the call you just placed. |
Status(...) → TTSStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The call this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Calls successfully completed / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<TTSRecipientResult> | Per-recipient results. See table below. |
TTSRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. TTS calls report as Voice. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's phone number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable call result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the call was actually placed to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this call, if available. |
Price | decimal? | Per-recipient cost. |
Reschedule(...)/Abort(...)/Resubmit(...)/Pacing(...) → TTSActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The call this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Pacing". |
Voice (Pre-Recorded Audio)
Deliver impactful messages by sending pre-recorded audio files as voice calls. Useful for alerts where a human voice adds a personal touch, or for conveying complex information that's easier to hear than read. Manage calls to individuals or large groups, with retry attempts for failed calls (RetryAttempts/RetryPeriod), answering machine detection, and interactive keypad responses.
Voice shares its Keypad model/enums with TTS (KeypadModel, Enums.AnswerPhoneMode, Enums.KeypadPlaySection). The builder API is identical.
Quick Example
var response = client.Messaging.Voice.SendMessage(
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (VoiceModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
TemplateID | string? | Yes* | Pre-configured audio template ID. |
MessageToPeople‡ | string? | No | Present for parity with TTSModel. A plain Voice send typically relies on TemplateID for its pre-recorded audio instead. |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
MessageToAnswerPhones‡ | string? | No | Alternative audio played when an answering machine picks up. |
AnswerPhoneMode | Enums.AnswerPhoneMode? | No | How to handle an answering machine. |
Keypads | ICollection<KeypadModel>? | No | Keypad menu options, same shape as TTS's. See Keypad fields below. |
VoiceFiles | ICollection<VoiceFileModel>? | No | Named audio clips (Name + base64-encoded File), added via VoiceBuilder.AddVoiceFile(name, file). TNZ hasn't published how these interact with keypad-triggered playback; use Keypads' own PlayFile/File pair (above) for that. |
KeypadOptionRequired | bool | No | Force the caller to press a key before the call proceeds. Default false. |
CallRouteMessageOnWrongKey‡ | string? | No | Message played if an invalid key is pressed. |
CallRouteMessageToPeople‡ | string? | No | Message played before routing to an operator. |
CallRouteMessageToOperators‡ | string? | No | Message played to the operator receiving the routed call. |
EndCallMessage‡ | string? | No | Message played at the end of the call, after any keypad routing. |
NumberOfOperators | int? | No | Number of simultaneous operators for keypad-routed calls. |
RetryAttempts | int? | No | Number of retry attempts on no-answer/busy. |
RetryPeriod | int? | No | Minutes between retry attempts. |
CallerID | string? | No | Caller ID shown to the recipient. |
Options | string? | No | Additional provider-specific options. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
ChargeCode | string? | No | Billing charge code for this send. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*The pre-recorded audio itself is normally configured via TemplateID (built in the Dashboard).
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via VoiceBuilder.AddDestination(...)/AddDestinations(...).
‡Takes base64-encoded WAV audio content directly, not a file path. See the call-flow sample below.
Named-parameter shortcut: client.Messaging.Voice.SendMessage(...) also accepts messageToPeople, destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, notificationType, sendMode directly (the model retains MessageToPeople for parity with TTS, but a plain Voice send typically relies on TemplateID instead). For keypads and every other field above, use VoiceBuilder or construct a VoiceModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Keypad Fields (KeypadModel)
| Field | Type | Description |
|---|---|---|
Tone | int | The DTMF digit this entry responds to. |
Play | string? | Message played when this key is pressed. |
RouteNumber | string? | Phone number to route the call to when this key is pressed. |
PlaySection | Enums.KeypadPlaySection? | Where in the call flow this keypad applies (e.g. Main). |
PlayFile | string? | Label identifying the audio clip played when this key is pressed, paired with File. Meaningful for Voice only, has no effect on TTS. |
File | string? | Base64-encoded WAV audio content played for this key, paired with PlayFile. Meaningful for Voice only, has no effect on TTS. |
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. Voice reads MainPhone for its destination; other address fields (ToNumber, MobilePhone, EmailAddress, FaxNumber) are accepted but not read by Voice.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand. Voice treats this as the destination phone number when no more specific field is set. Set by the single-string constructor. |
ToNumber | Accepted but not read by Voice (used by SMS/WhatsApp/RCS). |
MobilePhone | Accepted but not read by Voice (used by SMS/WhatsApp/RCS). |
MainPhone | Destination phone number, e.g. "+64211111111". Read by Voice. |
EmailAddress | Accepted but not read by Voice (used by Email). |
FaxNumber | Accepted but not read by Voice (used by Fax). |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Arbitrary per-destination personalisation values, [[Custom1]] … [[Custom9]]. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination shorthand
The simplest way to send: pass a destination directly to SendMessage(...). The pre-recorded audio comes from TemplateID, typically built in the Dashboard.
var response = client.Messaging.Voice.SendMessage(
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
With a keypad menu, via the builder
Add interactive keypad options so callers can route themselves to a different number by pressing a key.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination("+64211111111")
.AddKeypad(new KeypadModel
{
Tone = 1,
RouteNumber = "+64211112222",
PlaySection = Enums.KeypadPlaySection.Main
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
Call flow with pre-recorded audio and keypad-triggered playback
Set audio for every stage of the call: the main message, the answering machine fallback, the routing announcements, and the end-of-call message, alongside a keypad menu where key 1 routes to another number and key 2 plays its own audio clip. MessageToPeople, MessageToAnswerPhones, the CallRouteMessage* fields, and EndCallMessage all take base64-encoded WAV audio content directly, not a file path.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination("+64211111111")
.SetMessageToPeople("[base64-encoded-wav-data]")
.SetMessageToAnswerPhones("[base64-encoded-wav-data]")
.SetAnswerPhoneMode(Enums.AnswerPhoneMode.DAS)
.SetCallRouteMessageToPeople("[base64-encoded-wav-data]")
.SetCallRouteMessageToOperators("[base64-encoded-wav-data]")
.SetCallRouteMessageOnWrongKey("[base64-encoded-wav-data]")
.SetEndCallMessage("[base64-encoded-wav-data]")
.AddKeypad(new KeypadModel
{
Tone = 1,
RouteNumber = "+64211112222",
PlaySection = Enums.KeypadPlaySection.Main
})
.AddKeypad(new KeypadModel
{
Tone = 2,
PlayFile = "sales-greeting",
File = "[base64-encoded-wav-data]"
})
.SetKeypadOptionRequired(true)
.SetNumberOfOperators(2)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
Multiple destinations
Call more than one number in a single request using VoiceBuilder.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
With personalisation
Build a Destination with MainPhone and personalisation fields set. These are echoed back in the recipient's Status result even though Voice's own pre-recorded audio can't substitute merge tags into speech.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination(new Destination
{
MainPhone = "+64211111111",
FirstName = "Alice",
Custom1 = "Account #4432"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
Scheduled send
Delay the call to a specific SendTime.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination("+64211111111")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
Retry attempts, caller ID, and billing codes
Configure retry behaviour for calls that go unanswered or reach a busy signal, the caller ID shown to the recipient, and the sub-account/department/charge code used for billing and reporting.
using var builder = new VoiceBuilder();
var model = builder
.AddDestination("+64211111111")
.SetRetryAttempts(3)
.SetRetryPeriod(5)
.SetCallerID("+6499999999")
.SetSubAccount("SALES")
.SetDepartment("OUTBOUND")
.SetChargeCode("CC-001")
.SetReportTo("reports@example.com")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Voice.SendMessage(model);
Poll for status
Check call progress and per-recipient results any time after sending.
var status = client.Messaging.Voice.Status(response.MessageID);
if (status.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"JobStatus: '{status.JobStatus}', JobNum: '{status.JobNum}'");
foreach (var recipient in status.Recipients)
{
Console.WriteLine($" -> {recipient.Destination}: {recipient.Status} ({recipient.Result})");
}
}
Actions
Voice supports Reschedule, Abort, Resubmit, and Pacing (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-placed call to a new sendTime.
var response = client.Actions.Voice.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
Abort
Cancel a scheduled call before it's placed.
client.Actions.Voice.Abort(response.MessageID);
Resubmit
Resubmit a call for redelivery at a new scheduled time, without rebuilding the whole request.
client.Actions.Voice.Resubmit(response.MessageID, DateTime.Now.AddHours(1));
Pacing (adjust simultaneous operators)
Change how many calls run at once on an in-progress keypad-routed job.
client.Actions.Voice.Pacing(response.MessageID, numberOfOperators: 1);
Response
SendMessage(...) → VoiceApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the call you just placed. |
Status(...) → VoiceStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The call this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Calls successfully completed / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<VoiceRecipientResult> | Per-recipient results. See table below. |
VoiceRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's phone number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable call result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the call was actually placed to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this call, if available. |
Price | decimal? | Per-recipient cost. |
Reschedule(...)/Abort(...)/Resubmit(...)/Pacing(...) → VoiceActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The call this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Pacing". |
Fax
Bridge the gap between digital and traditional documentation with TNZ's reliable Fax API. Send documents directly from your application to any fax machine worldwide, with no physical fax hardware required. Track the delivery status of each fax to confirm your important documents arrive successfully.
Unlike SMS/RCS/WhatsApp, Fax has no free-text message field. Content comes entirely from an attachment.
Quick Example
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (FaxModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
Files | ICollection<Attachment>? | Yes* | The document(s) to fax. |
TemplateID | string? | Yes* | Pre-configured fax template ID (alternative to Files). |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
CSID | string? | No | Fax CSID (station identifier). |
Resolution | Enums.FaxResolution? | No | Fax output resolution. |
WatermarkFolder | string? | No | Folder containing watermark images. |
WatermarkFirstPage | string? | No | Watermark image applied to the first page only. |
WatermarkAllPages | string? | No | Watermark image applied to every page. |
RetryAttempts | int? | No | Number of retry attempts on busy/no-answer. |
RetryPeriod | int? | No | Minutes between retry attempts. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
ChargeCode | string? | No | Billing charge code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*Either Files or TemplateID must be provided.
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via FaxBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.Fax.SendMessage(...) also accepts destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, file, attachments, notificationType, sendMode directly. For CSID, Resolution, watermarks, retry settings, etc. use FaxBuilder or construct a FaxModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. Fax reads FaxNumber as the destination; the generic Recipient shorthand (set by AddDestination(string recipient) or the single-string constructor) is also accepted and treated as the fax number when sending via Fax.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand for the fax number. Set by AddDestination(string recipient) or the Destination single-string/named-argument constructor. |
ToNumber | Accepted but not read by Fax. |
MobilePhone | Accepted but not read by Fax. |
MainPhone | Accepted but not read by Fax. |
EmailAddress | Accepted but not read by Fax. |
FaxNumber | Destination fax number, e.g. "+6491111111". Read by Fax. |
Company | For reference/reporting. Fax has no message body to personalise. |
Attention | For reference/reporting. Fax has no message body to personalise. |
FirstName | For reference/reporting. Fax has no message body to personalise. |
LastName | For reference/reporting. Fax has no message body to personalise. |
Custom1–Custom9 | For reference/reporting. Fax has no message body to personalise. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination with an attachment
The simplest way to send: attach a document's base64-encoded content directly.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
From a file path
Let AddAttachment read and base64-encode a local file for you, instead of encoding it yourself.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment(@"C:\Documents\MyDocument.pdf") // reads and base64-encodes the file
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Multiple destinations
Send the same document to more than one fax number in a single request.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddDestination("+6492222222")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
With reference fields
Build a Destination with FaxNumber and reference fields set. Fax has no message body to personalise, so these are for reference and reporting rather than merge tags.
using var builder = new FaxBuilder();
var model = builder
.AddDestination(new Destination
{
FaxNumber = "+6491111111",
Attention = "Accounts Payable",
Custom1 = "Invoice #1234"
})
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Scheduled send
Delay delivery to a specific SendTime.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Setting resolution and CSID
Set the output resolution and the CSID (station identifier) printed in the header of the received fax.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetResolution(Enums.FaxResolution.High)
.SetCSID("MY COMPANY")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Adding a watermark
Apply an image from a watermark folder to the first page only, or to every page, of the outgoing fax.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetWatermarkFolder("Confidential")
.SetWatermarkFirstPage("confidential-stamp.png")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Configuring retry behaviour
Set how many times to retry a busy or unanswered fax number, and how many minutes to wait between attempts.
using var builder = new FaxBuilder();
var model = builder
.AddDestination("+6491111111")
.AddAttachment("My Document.pdf", "[base64-encoded-file-data]")
.SetRetryAttempts(3)
.SetRetryPeriod(5)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.Fax.SendMessage(model);
Poll for status
Check delivery progress and per-recipient results any time after sending.
var status = client.Messaging.Fax.Status(response.MessageID);
if (status.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"JobStatus: '{status.JobStatus}', JobNum: '{status.JobNum}'");
foreach (var recipient in status.Recipients)
{
Console.WriteLine($" -> {recipient.Destination}: {recipient.Status} ({recipient.Result})");
}
}
Actions
Fax supports Reschedule, Abort, and Resubmit (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-sent fax to a new sendTime.
var response = client.Actions.Fax.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
Abort
Cancel a scheduled fax before it sends.
client.Actions.Fax.Abort(response.MessageID);
Resubmit
Resend a fax at a new scheduled time without rebuilding the whole request.
client.Actions.Fax.Resubmit(response.MessageID, DateTime.Now.AddHours(1));
Response
SendMessage(...) → FaxApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the fax you just sent. |
Status(...) → FaxStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The fax this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Recipients successfully delivered / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<FaxRecipientResult> | Per-recipient results. See table below. |
FaxRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's fax number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable delivery result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the fax was actually sent to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this delivery, if available. |
Price | decimal? | Per-recipient cost. |
Reschedule(...)/Abort(...)/Resubmit(...) → FaxActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The fax this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Resubmit". |
WhatsApp is the world's most popular messaging platform. Reach users worldwide with text and files, and receive their responses back (see Poll for status / inbound messages below).
Send WhatsApp template messages, with optional fallback to another channel, via the TNZ REST API. WhatsApp requires both Message and TemplateID (not either/or like SMS). Find your Template ID in the Dashboard.
Quick Example
var response = client.Messaging.WhatsApp.SendMessage(
messageText: "Your order has shipped!",
templateId: "123e4567-e89b-12d3-a456-426614174000",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (WhatsAppModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
Message | string? | Yes | Message body. Supports personalisation tokens [[FirstName]], [[Custom1]], etc. |
TemplateID | string? | Yes | Pre-approved WhatsApp template ID, required alongside Message, unlike SMS/RCS's either/or. |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
FromNumber | string? | No | Sender ID shown on the recipient's device. |
FallbackMode | ICollection<Enums.WhatsAppFallbackMode>? | No | Fallback channel(s) if WhatsApp delivery fails, e.g. SMS. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
Files | ICollection<Attachment>? | No | Media attachments. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via WhatsAppBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.WhatsApp.SendMessage(...) also accepts messageText, templateId, destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, file, attachments, notificationType, sendMode directly. For FallbackMode, SendTime, SubAccount, etc. use WhatsAppBuilder or construct a WhatsAppModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. WhatsApp reads ToNumber/MobilePhone as the destination.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand for the destination phone number, e.g. "+64211111111". Read by WhatsApp the same way as ToNumber/MobilePhone. |
ToNumber | Destination phone number, e.g. "+64211111111". Read by WhatsApp. |
MobilePhone | Alternative phone destination field alongside ToNumber. Read by WhatsApp. |
MainPhone | Accepted but not read by WhatsApp. |
EmailAddress | Accepted but not read by WhatsApp. |
FaxNumber | Accepted but not read by WhatsApp. |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Personalisation tokens [[Custom1]]–[[Custom9]]. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination shorthand
The simplest way to send: pass the message, template, and destination directly to SendMessage(...). Remember both messageText and templateId are required.
var response = client.Messaging.WhatsApp.SendMessage(
messageText: "Your order has shipped!",
templateId: "123e4567-e89b-12d3-a456-426614174000",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
With SMS fallback, via the builder
Use WhatsAppBuilder to add a fallback channel for recipients that can't be reached on WhatsApp.
using var builder = new WhatsAppBuilder();
var model = builder
.SetMessageText("Your order has shipped!")
.SetTemplateID("123e4567-e89b-12d3-a456-426614174000")
.AddDestination("+64211111111")
.AddFallbackMode(Enums.WhatsAppFallbackMode.SMS)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.WhatsApp.SendMessage(model);
Multiple destinations
Send the same templated message to more than one destination in a single request.
using var builder = new WhatsAppBuilder();
var model = builder
.SetMessageText("Your order has shipped!")
.SetTemplateID("123e4567-e89b-12d3-a456-426614174000")
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.WhatsApp.SendMessage(model);
With personalisation
Build a Destination with ToNumber and personalisation fields set, for use with [[FirstName]]-style tokens in the message.
using var builder = new WhatsAppBuilder();
var model = builder
.SetMessageText("Hi [[FirstName]], your order #[[Custom1]] has shipped!")
.SetTemplateID("123e4567-e89b-12d3-a456-426614174000")
.AddDestination(new Destination
{
ToNumber = "+64211111111",
FirstName = "Alice",
Custom1 = "4432"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.WhatsApp.SendMessage(model);
With an attachment
Attach a file using its base64-encoded content.
using var builder = new WhatsAppBuilder();
var model = builder
.SetMessageText("Here's your invoice.")
.SetTemplateID("123e4567-e89b-12d3-a456-426614174000")
.AddDestination("+64211111111")
.AddAttachment("Invoice.pdf", "[base64-encoded-file-data]")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.WhatsApp.SendMessage(model);
Poll for status / inbound messages
Check delivery progress, or retrieve inbound replies received in the last timePeriod minutes, any time after sending.
var status = client.Messaging.WhatsApp.Status(response.MessageID);
var received = client.Messaging.WhatsApp.Received(timePeriod: 1440);
if (received.Result == Enums.ResultCode.Success)
{
foreach (var message in received.Messages)
{
Console.WriteLine($" => From: '{message.From}', MessageText: '{message.MessageText}'");
}
}
Actions
WhatsApp supports Reschedule and Abort (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-sent message to a new sendTime.
var response = client.Actions.WhatsApp.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
Abort
Cancel a scheduled message before it sends.
client.Actions.WhatsApp.Abort(response.MessageID);
Response
SendMessage(...) → WhatsAppApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the message you just sent. |
Status(...) → WhatsAppStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The message this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Recipients successfully delivered / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<WhatsAppRecipientResult> | Per-recipient results. See table below. |
WhatsAppRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's phone number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable delivery result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the message was actually sent to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this delivery, if available. |
Price | decimal? | Per-recipient cost. |
SMSReplies | List<WhatsAppReplyResult> | Inbound replies from this recipient. See table below. The property is named SMSReplies in the SDK even on WhatsApp; it isn't a mistake in this doc. |
WhatsAppReplyResult (each entry in SMSReplies)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this reply. |
ReceivedTimeLocal / ReceivedTimeUTC | DateTime? | When the reply was received. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
From | string? | The replying phone number. |
MessageText | string? | The reply body. |
Received(...) → WhatsAppReceivedApiResult
| Field | Type | Description |
|---|---|---|
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Messages. |
Messages | List<WhatsAppReceivedMessage> | Inbound WhatsApp messages. See table below. |
WhatsAppReceivedMessage (each entry in Messages)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this message. |
MessageID | MessageID? | The original outbound message this replies to, if determinable. |
JobNum | string? | The original send job's number, if applicable. |
SubAccount / Department | string? | Echoed billing codes from the original send. |
ReceivedTimeLocal / ReceivedTimeUTC / ReceivedTimeUTC_RFC3339 | DateTime? | When the message was received, in three formats. |
From | string? | The sender's phone number. |
ContactID | ContactID? | Addressbook contact reference, if the sender matched one. |
MessageText | string? | The message body. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
Version | string? | Payload format version. |
Reschedule(...)/Abort(...) → WhatsAppActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The message this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Reschedule". |
RCS
Regional Availability
RCS is not supported in New Zealand or Australia. Confirm destination coverage before relying on RCS as a primary channel. Consider Workflow to route messages to another channel where RCS isn't available.
Boost your messaging beyond traditional SMS with Rich Communication Services (RCS): send media-rich messages for a more engaging experience. Like SMS, Message/TemplateID are either/or (not both required, unlike WhatsApp).
Quick Example
var response = client.Messaging.RCS.SendMessage(
messageText: "Test RCS message",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Success - MessageID: {response.MessageID}");
}
Parameters (RCSModel)
| Parameter | Type | Required | Description |
|---|---|---|---|
Message | string? | Yes* | Message body. Supports personalisation tokens [[FirstName]], [[Custom1]], etc. |
TemplateID | string? | Yes* | Pre-configured message template ID (alternative to Message). |
Destinations | ICollection<Destination>? | Yes† | One or more destinations. See Destination fields below. |
ContactID | ContactID? | No | Single addressbook contact to send to (alternative/addition to Destinations). |
GroupID | GroupID? | No | Single addressbook group to send to (alternative/addition to Destinations). |
Reference | string? | No | Your internal reference, returned in reports and webhooks. |
FromNumber | string? | No | Sender ID shown on the recipient's device. |
FallbackMode | ICollection<Enums.RCSFallbackMode>? | No | Fallback channel(s) if RCS delivery fails, e.g. SMS, Voice, or WhatsApp. |
SendTime | DateTime? | No | Schedule delivery. Combine with Timezone. |
Timezone | string? | No | Windows Timezone name for SendTime. |
SubAccount | string? | No | Sub-account code for billing separation. |
Department | string? | No | Department code. |
MessageID | MessageID? | No | Supply your own message ID (otherwise auto-generated). |
ReportTo | string? | No | Email address to receive delivery reports. |
WebhookCallbackURL | string? | No | URL for delivery status callbacks. |
WebhookCallbackFormat | Enums.WebhookCallbackType? | No | Callback format. |
NotificationType | Enums.NotificationType? | No | Notification delivery mode. |
SMSEmailReply | string? | No | Email address to receive replies. |
CharacterConversion | bool | No | Convert characters outside the GSM character set automatically. Default false. |
Files | ICollection<Attachment>? | No | Media attachments. |
SendMode | Enums.SendModeType | No | Set Test to validate without sending. Default Live. |
*Either Message or TemplateID must be provided.
†Set directly via Destinations, via the named SendMessage(...) shortcut's toNumber/contactID/groupID/contactIDs/groupIDs/destination parameters, or via RCSBuilder.AddDestination(...)/AddDestinations(...).
Named-parameter shortcut: client.Messaging.RCS.SendMessage(...) also accepts messageText, destination, toNumber, contactID, groupID, contactIDs, groupIDs, destinations, file, attachments, notificationType, sendMode directly. For SendTime, SubAccount, SMSEmailReply, etc. use RCSBuilder or construct an RCSModel directly. destination accepts either a plain string or a Destination object directly; anything else throws ArgumentException.
Destination Fields (Destination)
Destination is shared across every channel; see the Destination Model reference in Getting Started for its full field list and construction patterns. RCS reads ToNumber/MobilePhone as the destination.
| Field | Description |
|---|---|
Recipient | Generic single-value shorthand. RCS treats this as the destination phone number when no more specific field is set. Set by the single-string constructor. |
ToNumber | Destination phone number, e.g. "+64211111111". Read by RCS. |
MobilePhone | Alternative phone destination field, also read by RCS. |
MainPhone | Accepted but not read by RCS (used by TTS/Voice). |
EmailAddress | Accepted but not read by RCS (used by Email). |
FaxNumber | Accepted but not read by RCS (used by Fax). |
Company | Personalisation token [[Company]]. |
Attention | Personalisation token [[Attention]]. |
FirstName | Personalisation token [[FirstName]]. |
LastName | Personalisation token [[LastName]]. |
Custom1–Custom9 | Arbitrary per-destination personalisation values, [[Custom1]] … [[Custom9]]. |
ContactID | Addressbook contact reference. Sends to that contact instead of a raw number. |
GroupID | Addressbook group reference. Sends to all members of that group. |
GroupCode | Alternative group lookup by code. |
Code Samples
Single destination shorthand
The simplest way to send: pass a message and destination directly to SendMessage(...).
var response = client.Messaging.RCS.SendMessage(
messageText: "Test RCS message",
toNumber: "+64211111111",
sendMode: Enums.SendModeType.Test
);
Via the builder, with a custom sender ID
Use RCSBuilder to set fields the named-parameter shortcut doesn't cover, such as a custom FromNumber sender ID.
using var builder = new RCSBuilder();
var model = builder
.SetMessageText("Test RCS message")
.SetFromNumber("61410023004") // Sender ID, E.164 without leading '+'
.AddDestination("+64211111111")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.RCS.SendMessage(model);
With SMS fallback
Add one or more fallback channels with AddFallbackMode(...), tried in order if RCS delivery fails.
using var builder = new RCSBuilder();
var model = builder
.SetMessageText("Test RCS message")
.AddDestination("+64211111111")
.AddFallbackMode(Enums.RCSFallbackMode.SMS)
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.RCS.SendMessage(model);
Multiple destinations
Send the same message to more than one destination in a single request.
using var builder = new RCSBuilder();
var model = builder
.SetMessageText("Test RCS message")
.AddDestination("+64211111111")
.AddDestination("+64222222222")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.RCS.SendMessage(model);
With personalisation
Build a Destination with ToNumber and personalisation fields set, for use with [[FirstName]]-style tokens in the message.
using var builder = new RCSBuilder();
var model = builder
.SetMessageText("Hi [[FirstName]], your order #[[Custom1]] has shipped!")
.AddDestination(new Destination
{
ToNumber = "+64211111111",
FirstName = "Alice",
Custom1 = "4432"
})
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.RCS.SendMessage(model);
Scheduled send
Delay delivery to a specific SendTime.
using var builder = new RCSBuilder();
var model = builder
.SetMessageText("Your reminder.")
.AddDestination("+64211111111")
.SetSendTime(DateTime.Now.AddDays(1))
.SetTimezone("New Zealand")
.SetSendMode(Enums.SendModeType.Test)
.Build();
var response = client.Messaging.RCS.SendMessage(model);
Poll for status / inbound messages
Check delivery progress, or retrieve inbound replies received in the last timePeriod minutes, any time after sending.
var status = client.Messaging.RCS.Status(response.MessageID);
var received = client.Messaging.RCS.Received(timePeriod: 1440);
if (received.Result == Enums.ResultCode.Success)
{
foreach (var message in received.Messages)
{
Console.WriteLine($" => From: '{message.From}', MessageText: '{message.MessageText}'");
}
}
Actions
RCS supports Reschedule and Abort (see the Getting Started capability table for all channels).
Reschedule
Move a not-yet-sent message to a new sendTime.
var response = client.Actions.RCS.Reschedule(
messageID: response.MessageID,
sendTime: DateTime.Parse("2026-12-31T12:00:00")
);
Abort
Cancel a scheduled message before it sends.
client.Actions.RCS.Abort(response.MessageID);
Response
SendMessage(...) → RCSApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
MessageID | MessageID? | The ID of the message you just sent. |
Status(...) → RCSStatusApiResult
| Field | Type | Description |
|---|---|---|
MessageID | MessageID? | The message this status is for. |
JobStatus | Enums.JobStatus | See Getting Started's Common Response Enums. |
JobNum | string? | TNZ's internal job number for this send. |
Account | string? | The TNZ account that owns this job. |
SubAccount / Department | string? | Echoed from the original send. |
Reference | string? | Echoed from the Reference parameter. |
CreatedTimeLocal / CreatedTimeUTC | DateTime? | When the job was created, in local time and UTC. |
DelayedTimeLocal / DelayedTimeUTC | DateTime? | The scheduled send time, if SendTime was set. |
Timezone | string? | Windows Timezone name used for scheduling. |
Count | int | Total recipients in the job. |
Complete | int | Recipients processed so far. |
Success / Failed | int | Recipients successfully delivered / failed. |
Price | decimal? | Job total cost. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Recipients. |
Recipients | List<RCSRecipientResult> | Per-recipient results. See table below. |
RCSRecipientResult (each entry in Recipients)
| Field | Type | Description |
|---|---|---|
Type | Enums.RecipientChannelType | See Getting Started's Common Response Enums. |
DestSeq | string? | TNZ's internal sequence ID for this recipient within the job. |
Destination | string? | The recipient's phone number. |
ContactID | ContactID? | Addressbook contact reference, if sent via ContactID/GroupID. |
Status | Enums.MessageStatus | See Getting Started's Common Response Enums. |
Result | string? | Human-readable delivery result for this recipient. |
SentTimeLocal / SentTimeUTC | DateTime? | When the message was actually sent to this recipient. |
Attention / Company / Custom1–Custom9 | string? | Echoed personalisation fields. See Destination Fields above. |
RemoteID | string? | Carrier/network-assigned identifier for this delivery, if available. |
Price | decimal? | Per-recipient cost. |
SMSReplies | List<RCSReplyResult> | Inbound replies from this recipient. See table below. The property is named SMSReplies in the SDK even on RCS; it isn't a mistake in this doc. |
RCSReplyResult (each entry in SMSReplies)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this reply. |
ReceivedTimeLocal / ReceivedTimeUTC | DateTime? | When the reply was received. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
From | string? | The replying phone number. |
MessageText | string? | The reply body. |
Received(...) → RCSReceivedApiResult
| Field | Type | Description |
|---|---|---|
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination metadata for Messages. |
Messages | List<RCSReceivedMessage> | Inbound RCS messages. See table below. |
RCSReceivedMessage (each entry in Messages)
| Field | Type | Description |
|---|---|---|
ReceivedID | ReceivedID? | Unique identifier for this message. |
MessageID | MessageID? | The original outbound message this replies to, if determinable. |
JobNum | string? | The original send job's number, if applicable. |
SubAccount / Department | string? | Echoed billing codes from the original send. |
ReceivedTimeLocal / ReceivedTimeUTC / ReceivedTimeUTC_RFC3339 | DateTime? | When the message was received, in three formats. |
From | string? | The sender's phone number. |
ContactID | ContactID? | Addressbook contact reference, if the sender matched one. |
MessageText | string? | The message body. |
Timezone | string? | Windows Timezone name for ReceivedTimeLocal. |
Version | string? | Payload format version. |
Reschedule(...)/Abort(...) → RCSActionApiResult
| Field | Type | Description |
|---|---|---|
ActionResult | string? | Human-readable result of the action. |
MessageID | MessageID? | The message this action was applied to. |
JobNum | string? | The job number this action was applied to. |
Status | Enums.JobStatus | See Getting Started's Common Response Enums. |
Action | string? | The action performed, e.g. "Reschedule". |
Inbound Webhooks
Get real-time updates without constant polling. TNZ's webhooks notify your application instantly when a message completes sending, or when you receive an inbound SMS. This event-driven approach keeps your system up to date with delivery receipts and customer replies, saving resources compared to polling Status(...)/Received(...) on a timer.
Webhooks are inbound: TNZ's servers POST these payloads to your own server (configure WebhookCallbackURL on a Send call, or your Sender's Dashboard settings for SMS-reply/result reporting). The SDK doesn't call anything for this; instead, TNZAPI.NET.Webhooks provides typed payload models you can deserialize an incoming request body into from your own webhook receiver endpoint.
Payload Fields
ResultWebhookPayload and InboundSMSWebhookPayload share nearly identical shapes, with two exceptions worth knowing about: ReceivedID is string? on ResultWebhookPayload but the strongly-typed ReceivedID? on InboundSMSWebhookPayload; and Status is the strongly-typed Enums.MessageStatus? on ResultWebhookPayload but plain string? on InboundSMSWebhookPayload.
| Field | Type | Description |
|---|---|---|
Version | string? | Webhook payload format version. |
Sender | string? | Your TNZ Sender ID. |
APIKey | string? | Your API key, included for correlation/validation. |
Type | string? | Message channel type (e.g. "SMS"). |
Destination | string? | The recipient (or, for inbound SMS, the sender) address/number. |
ContactID | ContactID? | Addressbook contact reference, if the destination matched one. |
ReceivedID | See note above | Identifier for this specific inbound/result event. |
MessageID | MessageID? | The original outbound message this event relates to. |
SubAccount | string? | Sub-account code, echoed from the original send. |
Department | string? | Department code, echoed from the original send. |
JobNumber | string? | Job number for the send batch. |
SentTimeLocal / SendTimeUTC / SentTimeUTC_RFC3339 | DateTime? | Event timestamp in local time, UTC, and RFC3339 UTC respectively. |
Status | See note above | Delivery/message status. |
Result | string? | Result code/description for this event. |
Message | string? | The message text (the reply body for inbound SMS). |
Price | string? | See the Price note below. |
Detail | string? | Additional detail text for this event. |
URL | string? | Related URL, if applicable. |
Code Samples
Delivery result webhook
Handle the payload TNZ posts when a message completes sending, whatever the outcome, as an alternative to polling Status(...).
// Inside your own ASP.NET (or similar) webhook receiver action:
var payload = System.Text.Json.JsonSerializer.Deserialize<TNZAPI.NET.Webhooks.ResultWebhookPayload>(requestBody);
Console.WriteLine($"{payload.Type} to {payload.Destination}: {payload.Status} ({payload.Result})");
Inbound SMS webhook
Handle the payload TNZ posts when a recipient replies to an SMS, as an alternative to polling Received(...).
var inboundSms = System.Text.Json.JsonSerializer.Deserialize<TNZAPI.NET.Webhooks.InboundSMSWebhookPayload>(requestBody);
Console.WriteLine($"Inbound SMS from {inboundSms.Destination}: {inboundSms.Message}");
Both payload types deserialize correctly with your own default JsonSerializerOptions. No need to configure anything on your end, including TNZ's non-standard timestamp format.
Note: Price on both payload types is string?, not decimal?. TNZ's webhook callback may send it as either a JSON string or a JSON number, and the SDK tolerantly parses either into a string so you get a consistent type either way. This differs from the decimal? Price exposed by each channel's Status(...) result (job total and per-recipient). Parse it yourself, e.g. decimal.Parse(payload.Price, CultureInfo.InvariantCulture), if you need a numeric value from a webhook payload.
Addressbook
Requires Address Book API Access
Your API user needs this permission enabled separately from general API access: Dashboard → Users → API User → API → Address Book API Access.
Centralise your contacts with the Addressbook: a single source of truth that simplifies your integration and enables data-rich personalisation across every messaging channel. Manage contacts, groups, and contact–group relationships; keep contact data synchronised with your CRM, HR system, or spreadsheets; organise contacts into groups to message thousands with a single GroupID; and reduce payload size by referencing a ContactID/GroupID instead of sending full recipient details on every send. Personalisation fields (FirstName, Company, Custom1–Custom4) work as [[FirstName]]-style merge tags in your message body, and contacts/groups created here are also usable directly in the TNZ Dashboard. Groups have no custom fields of their own; personalisation always comes from the contact.
Contact
Fields (ContactModel)
| Field | Type | Description |
|---|---|---|
ExType | string | External system type tag, for correlating this contact with your own CRM/system. |
ExID | string | External system ID, for correlating this contact with your own CRM/system. |
ViewBy | Enums.ViewEditByOptions? | Who can view this contact in the Dashboard: Account, SubAccount, Department, or No. |
EditBy | Enums.ViewEditByOptions? | Who can edit this contact in the Dashboard, same values as ViewBy. |
AccessControl | Enums.AccessControlLevel? | Limited or Granted. |
Attention | string | Personalisation token [[Attention]]. |
Title | string | e.g. "Mr", "Dr". |
Company | string | Personalisation token [[Company]]. |
RecipDepartment | string | The contact's department at their company. Not related to your TNZ Department code. |
FirstName | string | Personalisation token [[FirstName]]. |
LastName | string | Personalisation token [[LastName]]. |
Position | string | Job title. |
StreetAddress / Suburb / City / State / Country / Postcode | string | Postal address fields. |
MainPhone | string | Primary phone number. |
DirectPhone | string | Direct-dial phone number. |
AltPhone1–AltPhone8 | string | Up to 8 additional phone numbers. |
MobilePhone | string | Mobile number, used as the SMS/WhatsApp/RCS destination when sending via ContactID. |
FaxNumber | string | Fax destination. |
EmailAddress | string | Email destination. |
WebAddress | string | Website URL. |
Custom1–Custom4 | string | Personalisation tokens [[Custom1]]–[[Custom4]]. |
Notes | string | Free-text notes. Not exposed as a personalisation token. |
Create
Build a contact with ContactBuilder and save it to your Addressbook.
using var builder = new ContactBuilder();
var contact = builder
.SetAttention("API Test")
.SetFirstName("API")
.SetLastName("Test")
.SetMobilePhone("+64211231234")
.SetEmailAddress("test@example.com")
.SetMainPhone("+6491112222")
.Build();
var response = client.Addressbook.Contact.Create(contact);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Created ContactID={response.ContactID}");
}
Details
Look up a contact's stored fields by ContactID.
var details = client.Addressbook.Contact.Details(response.ContactID);
if (details.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"{details.FirstName} {details.LastName}, {details.EmailAddress}");
}
Update and Delete
Change or remove a contact by ContactID.
var updated = client.Addressbook.Contact.Update(response.ContactID, new ContactModel
{
Company = "Example Company"
});
client.Addressbook.Contact.Delete(response.ContactID);
Search and List
Find contacts by any combination of emailAddress (full match) or mobilePhone/mainPhone/attention/firstName/lastName/company (partial match), or page through your full contact list.
var results = client.Addressbook.Contact.Search(
firstName: "Alice",
company: "Example Company",
page: 1,
recordsPerPage: 100
);
if (results.Result == Enums.ResultCode.Success)
{
foreach (var contact in results.Contacts)
{
Console.WriteLine($"{contact.ContactID}: {contact.FirstName} {contact.LastName}");
}
}
var page = client.Addressbook.Contact.List(page: 1, recordsPerPage: 100);
if (page.Result == Enums.ResultCode.Success)
{
foreach (var contact in page.Contacts)
{
Console.WriteLine($"{contact.ContactID}: {contact.FirstName} {contact.LastName}");
}
}
Group
Fields (GroupModel)
| Field | Type | Description |
|---|---|---|
GroupName | string | Display name for the group. |
SubAccount | string | Sub-account code. |
Department | string | Department code. |
ViewEditBy | Enums.ViewEditByOptions? | Who can view and edit this group in the Dashboard: Account, SubAccount, Department, or No. Unlike Contact, Group has one combined permission rather than separate ViewBy/EditBy fields. |
AccessControl | Enums.AccessControlLevel? | Limited or Granted. |
Create
Build a group with GroupBuilder and save it to your Addressbook. GroupCode is server-assigned; it's returned on the response, not something you set.
using var builder = new GroupBuilder();
var group = builder
.SetGroupName("API Test Group")
.SetSubAccount("SALES")
.SetViewEditBy(Enums.ViewEditByOptions.SubAccount)
.Build();
var response = client.Addressbook.Group.Create(group);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Created GroupID={response.GroupID}, GroupCode={response.GroupCode}");
}
Details, Update, Delete, and List
Manage a group the same way as a contact: look up, rename, remove, or page through all groups.
var details = client.Addressbook.Group.Details(response.GroupID);
client.Addressbook.Group.Update(response.GroupID, new GroupModel { GroupName = "Renamed Group" });
client.Addressbook.Group.Delete(response.GroupID);
var page = client.Addressbook.Group.List(page: 1, recordsPerPage: 100);
Contact ↔ Group Relationships
Both directions support the full set of operations: Contact.Group manages one contact's group memberships (list, add, remove, and detail), and Group.Contact manages one group's contact memberships the same way (list, add, remove, and detail).
// Groups a contact belongs to
var groups = client.Addressbook.Contact.Group.List(contactID);
// Add a contact to a group
var addResult = client.Addressbook.Contact.Group.Add(contactID, groupID);
if (addResult.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Added to group: {addResult.Group.GroupName}");
}
// Look up a single contact-group relation
var relation = client.Addressbook.Contact.Group.Detail(contactID, groupID);
// Remove a contact from a group
client.Addressbook.Contact.Group.Remove(contactID, groupID);
// Contacts belonging to a group
var contacts = client.Addressbook.Group.Contact.List(groupID);
if (contacts.Result == Enums.ResultCode.Success)
{
foreach (var contact in contacts.Contacts)
{
Console.WriteLine($"{contact.FirstName} {contact.LastName}");
}
}
// Add a contact to a group from the Group side
var groupAddResult = client.Addressbook.Group.Contact.Add(groupID, contactID);
if (groupAddResult.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Added {groupAddResult.Contact.FirstName} {groupAddResult.Contact.LastName} to group");
}
// Remove a contact from a group from the Group side
client.Addressbook.Group.Contact.Remove(groupID, contactID);
// Look up a single group-contact relation
var groupRelation = client.Addressbook.Group.Contact.Detail(groupID, contactID);
Response
Every Addressbook result implements IApiResult: check Result before reading other fields; see Getting Started. List-style results additionally carry TotalRecords/RecordsPerPage/PageCount/Page (all int) for pagination.
Contact.Create(...)/Details(...)/Update(...)/Delete(...) → ContactApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
ContactID | ContactID? | The contact's ID. |
Owner | string? | The TNZ user who owns this contact. |
CreatedTimeLocal / CreatedTimeUTC / CreatedTimeUTC_RFC3339 | DateTime? | When the contact was created, in local time, UTC, and RFC3339 UTC respectively. |
UpdatedTimeLocal / UpdatedTimeUTC / UpdatedTimeUTC_RFC3339 | DateTime? | When the contact was last updated. |
Timezone | string? | Timezone the local timestamps above are expressed in. |
every ContactModel field above | nullable equivalent | Echoed back, e.g. FirstName, EmailAddress, Custom1–Custom4. See the Fields table above. |
Contact.Search(...)/List(...) → ContactListApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
Contacts | List<ContactDetail> | The matching contacts for this page, each shaped like Contact.Details(...)'s result. |
Group.Create(...)/Details(...)/Update(...)/Delete(...) → GroupApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
GroupID | GroupID? | The group's ID. |
GroupCode | string? | Server-assigned lookup code. Read-only; there's no matching field on GroupModel to set it. |
GroupName / SubAccount / Department / ViewEditBy / AccessControl | nullable equivalent | Echoed back. See the Fields table above. |
Owner | string? | The TNZ user who owns this group. |
CreatedTimeLocal / CreatedTimeUTC / CreatedTimeUTC_RFC3339 | DateTime? | When the group was created. Unlike Contact, Group has no Updated* timestamps. |
Timezone | string? | Timezone the local timestamp above is expressed in. |
Group.List(...) → GroupListApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
Groups | List<GroupDetail> | The groups for this page, each shaped like Group.Details(...)'s result. |
Contact.Group.List(...) → ContactGroupListApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
Contact | ContactDetail? | The contact whose groups you're listing. |
Groups | List<GroupDetail> | The groups this contact belongs to. |
Group.Contact.List(...) → GroupContactListApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
Group | GroupDetail? | The group whose members you're listing. |
Contacts | List<ContactDetail> | The contacts belonging to this group. |
Contact.Group.Add(...)/Remove(...)/Detail(...) and Group.Contact.Add(...)/Remove(...)/Detail(...) → ContactGroupRelationApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
Group | GroupDetail? | The group side of this relation. |
Contact | ContactDetail? | The contact side of this relation. |
This result type carries no pagination fields, unlike the two List results above; it always describes exactly one contact–group relation.
OptOut
Programmatically manage contacts who have unsubscribed, helping you meet your obligations under anti-spam regulations. Opt-outs are managed on a SubAccount, Department, and message-type (DestType) basis: a contact can opt out of SMS marketing while still receiving important Email alerts, since each channel is suppressed independently.
Once a destination is opted out, sending to it doesn't fail outright: the API accepts the request, but delivery is blocked and the response reports a "Destination is blacklisted" result. This gives you a clear audit trail rather than a hard error. Use List(...) below to retrieve the full opt-out list for auditing, reporting, or syncing with your own CRM.
Enums.OptOutDestType covers the channels OptOut actually supports: SMS, Email, Voice, Fax. WhatsApp and RCS have no blacklist/suppression support in the backend; submitting either as a DestType throws a server-side error. OptOutBuilder.SetDestType(...) and List(...) both accept the enum directly, or a plain string if you need a value the enum doesn't cover.
Fields (OptOutModel)
| Field | Type | Description |
|---|---|---|
DestType | string | Required. The channel this entry applies to. See Enums.OptOutDestType above. |
Destination | string | The destination to suppress, e.g. "+6421003004" or an email address. Use Destination or ContactID, not both. |
ContactID | ContactID? | Opt out an addressbook contact instead of a raw destination. Use Destination or ContactID, not both. |
SubAccount | string | Scope this entry to a sub-account. Empty applies to all sub-accounts. |
Department | string | Scope this entry to a department. Empty applies to all departments. |
StopMessage | string | The opt-out phrase detected, e.g. "Stop sending me these messages". |
Notes | string | Free-text notes. |
Code Samples
Create a single OptOut entry
Suppress future sends to one destination on a specific channel, using Enums.OptOutDestType for the channel.
using var builder = new OptOutBuilder();
var entry = builder
.SetDestType(Enums.OptOutDestType.SMS)
.SetDestination("+6421003004")
.SetNotes("Requested via support call")
.Build();
var response = client.Configuration.OptOut.Create(entry);
if (response.Result == Enums.ResultCode.Success)
{
Console.WriteLine($"Created OptOut ID={response.ID}");
}
Opt out an addressbook contact
Suppress an addressbook contact by ContactID instead of a raw destination. Set ContactID or Destination, not both.
using var builder = new OptOutBuilder();
var entry = builder
.SetDestType(Enums.OptOutDestType.Email)
.SetContactID(contactID)
.Build();
var response = client.Configuration.OptOut.Create(entry);
Opt out multiple destinations at once
Suppress a batch of destinations on the same channel in a single call. OptOutBatchModel has no builder, so DestType stays a plain string here; use Enums.OptOutDestType's .ToString() for compile-time safety.
var batchResponse = client.Configuration.OptOut.CreateBatch(new OptOutBatchModel
{
DestType = Enums.OptOutDestType.SMS.ToString(),
Destinations = new List<string> { "+6421003004", "+6421003005" }
});
if (batchResponse.Result == Enums.ResultCode.Success)
{
foreach (var entry in batchResponse.OptOuts)
{
Console.WriteLine($"Opted out: {entry.Destination} ({entry.ID})");
}
}
Update
Change the notes or scoping on an existing entry by OptOutID. DestType and Destination/ContactID can be changed the same way, though moving an entry to a different destination is unusual; deleting and re-creating is more common for that case.
var updated = client.Configuration.OptOut.Update(response.ID, new OptOutModel
{
Notes = "Confirmed via follow-up call"
});
Details, Delete, and List
Look up a single entry, remove an opt-out, or page through the full suppression list, filtering by channel with Enums.OptOutDestType.
var details = client.Configuration.OptOut.Details(response.ID);
client.Configuration.OptOut.Delete(response.ID);
var list = client.Configuration.OptOut.List(destType: Enums.OptOutDestType.SMS, timePeriod: 30);
if (list.Result == Enums.ResultCode.Success)
{
foreach (var entry in list.OptOuts)
{
Console.WriteLine($"{entry.Destination}, {entry.DestType}");
}
}
Response
Every OptOut result implements IApiResult: check Result before reading other fields; see Getting Started.
Create(...)/Details(...)/Update(...)/Delete(...) → OptOutApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
ID | OptOutID? | This entry's ID. |
DestType / Destination / ContactID / SubAccount / Department / StopMessage / Notes | nullable equivalent | Echoed back. See the Fields table above. |
OriginalMessage | string? | The original inbound message that triggered the opt-out, when it was created automatically from a [[STOP]]-style reply rather than via this API. |
CreatedTimeLocal / CreatedTimeUTC / CreatedTimeUTC_RFC3339 | DateTime? | When the entry was created, in local time, UTC, and RFC3339 UTC respectively. |
UpdatedTimeLocal / UpdatedTimeUTC / UpdatedTimeUTC_RFC3339 | DateTime? | When the entry was last updated. |
Timezone | string? | Timezone the local timestamps above are expressed in. |
CreateBatch(...) → OptOutBatchApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
OptOuts | List<OptOutDetail> | One entry per destination in the batch, each shaped like Details(...)'s result. |
List(...) → OptOutListApiResult
| Field | Type | Description |
|---|---|---|
Result | Enums.ResultCode | See Getting Started. |
ErrorMessage | List<string> | See Getting Started. |
TotalRecords / RecordsPerPage / PageCount / Page | int | Pagination. |
OptOuts | List<OptOutDetail> | The matching entries for this page. |