Prerequisites
- A Postmark account with a verified sender signature
- Your Postmark Server API Token
Setup
1
Get your Postmark server token
In Postmark, open the server you send from and copy its Server API token.
2
Configure in Courier
Open the in Courier, enter your API token and From Address, then save.
Subject character limit
The Postmark API limits subject lines to ~60 characters. Emojis and special characters lower that limit because of encoding.
Overrides
covers the two levels and which one wins. lists the fields every email provider takes. A provider override changes what Courier sends to Postmark’s Email API. This example adds a MessageStream and an attachment:const { requestId } = await courier.send.message({
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
email: "sarah@acme-corp.com",
},
providers: {
postmark: {
override: {
config: {
MessageStream: "message_stream_id",
},
body: {
Attachments: [
{
Name: "readme.txt",
Content: "dGVzdCBjb250ZW50",
ContentType: "text/plain",
},
],
},
},
},
},
},
});
response = client.send.message(
message={
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"to": {
"email": "sarah@acme-corp.com",
},
"providers": {
"postmark": {
"override": {
"config": {
"MessageStream": "message_stream_id",
},
"body": {
"Attachments": [
{
"Name": "readme.txt",
"Content": "dGVzdCBjb250ZW50",
"ContentType": "text/plain",
},
],
},
},
},
},
},
)
curl -X POST https://api.courier.com/send \
-H "Authorization: Bearer $COURIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": {
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"to": {
"email": "sarah@acme-corp.com"
},
"providers": {
"postmark": {
"override": {
"config": {
"MessageStream": "message_stream_id"
},
"body": {
"Attachments": [
{
"Name": "readme.txt",
"Content": "dGVzdCBjb250ZW50",
"ContentType": "text/plain"
}
]
}
}
}
}
}
}'
response = courier.send_.message(
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
email: "sarah@acme-corp.com"
},
providers: {
postmark: {
override: {
config: {
MessageStream: "message_stream_id"
},
body: {
Attachments: [
{
Name: "readme.txt",
Content: "dGVzdCBjb250ZW50",
ContentType: "text/plain"
}
]
}
}
}
}
}
)
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{
OfUserRecipient: &shared.UserRecipientParam{
Email: courier.String("sarah@acme-corp.com"),
},
},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
Providers: shared.MessageProvidersParam{
"postmark": shared.MessageProvidersTypeParam{
Override: map[string]any{
"config": map[string]any{
"MessageStream": "message_stream_id",
},
"body": map[string]any{
"Attachments": []any{
map[string]any{
"Name": "readme.txt",
"Content": "dGVzdCBjb250ZW50",
"ContentType": "text/plain",
},
},
},
},
},
},
},
})
SendMessageParams params = SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
.to(UserRecipient.builder().email("sarah@acme-corp.com").build())
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.providers(MessageProviders.builder()
.putAdditionalProperty("postmark", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
"config", java.util.Map.of(
"MessageStream", "message_stream_id"
),
"body", java.util.Map.of(
"Attachments", java.util.List.of(
java.util.Map.of(
"Name", "readme.txt",
"Content", "dGVzdCBjb250ZW50",
"ContentType", "text/plain"
)
)
)
))))
.build())
.build())
.build();
SendMessageResponse response = client.send().message(params);
$response = $client->send->message(
message: [
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
'to' => [
'email' => 'sarah@acme-corp.com',
],
'providers' => [
'postmark' => [
'override' => [
'config' => [
'MessageStream' => 'message_stream_id',
],
'body' => [
'Attachments' => [
[
'Name' => 'readme.txt',
'Content' => 'dGVzdCBjb250ZW50',
'ContentType' => 'text/plain',
],
],
],
],
],
],
],
);
SendMessageParams parameters = new()
{
Message = new()
{
To = new UserRecipient { Email = "sarah@acme-corp.com" },
Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
Providers = new Dictionary<string, MessageProvidersType>()
{
{
"postmark",
new()
{
Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"config": {
"MessageStream": "message_stream_id"
},
"body": {
"Attachments": [
{
"Name": "readme.txt",
"Content": "dGVzdCBjb250ZW50",
"ContentType": "text/plain"
}
]
}
}
"""
),
}
},
},
},
};
var response = await client.Send.Message(parameters);
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"email": "sarah@acme-corp.com"}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
--message.providers '{"postmark": {"override": {"config": {"MessageStream": "message_stream_id"}, "body": {"Attachments": [{"Name": "readme.txt", "Content": "dGVzdCBjb250ZW50", "ContentType": "text/plain"}]}}}}'
With Courier MCP, send my template to sarah@acme-corp.com on my Postmark broadcast message stream.
override.config.fromAddress swaps the sender for one message, in place of the From Address saved on the integration. A template From value or a channels.email.override.from still wins over it. covers the order.
Using Postmark templates
const { requestId } = await courier.send.message({
message: {
to: {
email: "sarah@acme-corp.com",
},
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
providers: {
postmark: {
override: {
config: {
url: "https://api.postmarkapp.com/email/withTemplate",
},
body: {
TemplateId: "123456",
TemplateModel: {
product_url: "product_url_Value",
product_name: "product_name_Value",
name: "name_Value",
action_url: "action_url_Value",
login_url: "login_url_Value",
username: "username_Value",
trial_length: "trial_length_Value",
trial_start_date: "trial_start_date_Value",
trial_end_date: "trial_end_date_Value",
support_email: "support_email_Value",
live_chat_url: "live_chat_url_Value",
sender_name: "sender_name_Value",
help_url: "help_url_Value",
company_name: "company_name_Value",
company_address: "company_address_Value",
},
},
},
},
},
},
});
response = client.send.message(
message={
"to": {
"email": "sarah@acme-corp.com",
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"providers": {
"postmark": {
"override": {
"config": {
"url": "https://api.postmarkapp.com/email/withTemplate",
},
"body": {
"TemplateId": "123456",
"TemplateModel": {
"product_url": "product_url_Value",
"product_name": "product_name_Value",
"name": "name_Value",
"action_url": "action_url_Value",
"login_url": "login_url_Value",
"username": "username_Value",
"trial_length": "trial_length_Value",
"trial_start_date": "trial_start_date_Value",
"trial_end_date": "trial_end_date_Value",
"support_email": "support_email_Value",
"live_chat_url": "live_chat_url_Value",
"sender_name": "sender_name_Value",
"help_url": "help_url_Value",
"company_name": "company_name_Value",
"company_address": "company_address_Value",
},
},
},
},
},
},
)
curl -X POST https://api.courier.com/send \
-H "Authorization: Bearer $COURIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": {
"to": {
"email": "sarah@acme-corp.com"
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"providers": {
"postmark": {
"override": {
"config": {
"url": "https://api.postmarkapp.com/email/withTemplate"
},
"body": {
"TemplateId": "123456",
"TemplateModel": {
"product_url": "product_url_Value",
"product_name": "product_name_Value",
"name": "name_Value",
"action_url": "action_url_Value",
"login_url": "login_url_Value",
"username": "username_Value",
"trial_length": "trial_length_Value",
"trial_start_date": "trial_start_date_Value",
"trial_end_date": "trial_end_date_Value",
"support_email": "support_email_Value",
"live_chat_url": "live_chat_url_Value",
"sender_name": "sender_name_Value",
"help_url": "help_url_Value",
"company_name": "company_name_Value",
"company_address": "company_address_Value"
}
}
}
}
}
}
}'
response = courier.send_.message(
message: {
to: {
email: "sarah@acme-corp.com"
},
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
providers: {
postmark: {
override: {
config: {
url: "https://api.postmarkapp.com/email/withTemplate"
},
body: {
TemplateId: "123456",
TemplateModel: {
product_url: "product_url_Value",
product_name: "product_name_Value",
name: "name_Value",
action_url: "action_url_Value",
login_url: "login_url_Value",
username: "username_Value",
trial_length: "trial_length_Value",
trial_start_date: "trial_start_date_Value",
trial_end_date: "trial_end_date_Value",
support_email: "support_email_Value",
live_chat_url: "live_chat_url_Value",
sender_name: "sender_name_Value",
help_url: "help_url_Value",
company_name: "company_name_Value",
company_address: "company_address_Value"
}
}
}
}
}
}
)
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{
OfUserRecipient: &shared.UserRecipientParam{
Email: courier.String("sarah@acme-corp.com"),
},
},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
Providers: shared.MessageProvidersParam{
"postmark": shared.MessageProvidersTypeParam{
Override: map[string]any{
"config": map[string]any{
"url": "https://api.postmarkapp.com/email/withTemplate",
},
"body": map[string]any{
"TemplateId": "123456",
"TemplateModel": map[string]any{
"product_url": "product_url_Value",
"product_name": "product_name_Value",
"name": "name_Value",
"action_url": "action_url_Value",
"login_url": "login_url_Value",
"username": "username_Value",
"trial_length": "trial_length_Value",
"trial_start_date": "trial_start_date_Value",
"trial_end_date": "trial_end_date_Value",
"support_email": "support_email_Value",
"live_chat_url": "live_chat_url_Value",
"sender_name": "sender_name_Value",
"help_url": "help_url_Value",
"company_name": "company_name_Value",
"company_address": "company_address_Value",
},
},
},
},
},
},
})
SendMessageParams params = SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
.to(UserRecipient.builder().email("sarah@acme-corp.com").build())
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.providers(MessageProviders.builder()
.putAdditionalProperty("postmark", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
"config", java.util.Map.of(
"url", "https://api.postmarkapp.com/email/withTemplate"
),
"body", java.util.Map.of(
"TemplateId", "123456",
"TemplateModel", java.util.Map.of(
"product_url", "product_url_Value",
"product_name", "product_name_Value",
"name", "name_Value",
"action_url", "action_url_Value",
"login_url", "login_url_Value",
"username", "username_Value",
"trial_length", "trial_length_Value",
"trial_start_date", "trial_start_date_Value",
"trial_end_date", "trial_end_date_Value",
"support_email", "support_email_Value",
"live_chat_url", "live_chat_url_Value",
"sender_name", "sender_name_Value",
"help_url", "help_url_Value",
"company_name", "company_name_Value",
"company_address", "company_address_Value"
)
)
))))
.build())
.build())
.build();
SendMessageResponse response = client.send().message(params);
$response = $client->send->message(
message: [
'to' => [
'email' => 'sarah@acme-corp.com',
],
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
'providers' => [
'postmark' => [
'override' => [
'config' => [
'url' => 'https://api.postmarkapp.com/email/withTemplate',
],
'body' => [
'TemplateId' => '123456',
'TemplateModel' => [
'product_url' => 'product_url_Value',
'product_name' => 'product_name_Value',
'name' => 'name_Value',
'action_url' => 'action_url_Value',
'login_url' => 'login_url_Value',
'username' => 'username_Value',
'trial_length' => 'trial_length_Value',
'trial_start_date' => 'trial_start_date_Value',
'trial_end_date' => 'trial_end_date_Value',
'support_email' => 'support_email_Value',
'live_chat_url' => 'live_chat_url_Value',
'sender_name' => 'sender_name_Value',
'help_url' => 'help_url_Value',
'company_name' => 'company_name_Value',
'company_address' => 'company_address_Value',
],
],
],
],
],
],
);
SendMessageParams parameters = new()
{
Message = new()
{
To = new UserRecipient { Email = "sarah@acme-corp.com" },
Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
Providers = new Dictionary<string, MessageProvidersType>()
{
{
"postmark",
new()
{
Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"config": {
"url": "https://api.postmarkapp.com/email/withTemplate"
},
"body": {
"TemplateId": "123456",
"TemplateModel": {
"product_url": "product_url_Value",
"product_name": "product_name_Value",
"name": "name_Value",
"action_url": "action_url_Value",
"login_url": "login_url_Value",
"username": "username_Value",
"trial_length": "trial_length_Value",
"trial_start_date": "trial_start_date_Value",
"trial_end_date": "trial_end_date_Value",
"support_email": "support_email_Value",
"live_chat_url": "live_chat_url_Value",
"sender_name": "sender_name_Value",
"help_url": "help_url_Value",
"company_name": "company_name_Value",
"company_address": "company_address_Value"
}
}
}
"""
),
}
},
},
},
};
var response = await client.Send.Message(parameters);
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"email": "sarah@acme-corp.com"}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
--message.providers '{"postmark": {"override": {"config": {"url": "https://api.postmarkapp.com/email/withTemplate"}, "body": {"TemplateId": "123456", "TemplateModel": {"product_url": "product_url_Value", "product_name": "product_name_Value", "name": "name_Value", "action_url": "action_url_Value", "login_url": "login_url_Value", "username": "username_Value", "trial_length": "trial_length_Value", "trial_start_date": "trial_start_date_Value", "trial_end_date": "trial_end_date_Value", "support_email": "support_email_Value", "live_chat_url": "live_chat_url_Value", "sender_name": "sender_name_Value", "help_url": "help_url_Value", "company_name": "company_name_Value", "company_address": "company_address_Value"}}}}}'
With Courier MCP, send my template to sarah@acme-corp.com and render it with a Postmark template.
message.providers.postmark.override.config property replaces Courier templates with Postmark’s Templates API.
Include the https://api.postmarkapp.com/email/withTemplate URL in the config object, and TemplateId and TemplateModel in the body.
With Postmark templates, your request must use a
template parameter, not content. Otherwise content takes precedence over the Postmark override. An invalid template_id causes an error.Provider details
postmark
routing.channels instead is supported, and sends through just this provider.
Send to a specific provider
When that is worth doing, and what you give up: failover, channel priority, and providers you add later.