import Courier from '@trycourier/courier';
const client = new Courier({
apiKey: process.env['COURIER_API_KEY'], // This is the default and can be omitted
});
const putTenantTemplateResponse = await client.tenants.templates.replace('template_id', {
tenant_id: 'tenant_id',
template: {
content: { version: '2022-01-01', elements: [{ type: 'channel' }] },
routing: { method: 'single', channels: ['email'] },
},
published: true,
});
console.log(putTenantTemplateResponse.id);import os
from courier import Courier
client = Courier(
api_key=os.environ.get("COURIER_API_KEY"), # This is the default and can be omitted
)
put_tenant_template_response = client.tenants.templates.replace(
template_id="template_id",
tenant_id="tenant_id",
template={
"content": {
"version": "2022-01-01",
"elements": [{
"type": "channel"
}],
},
"routing": {
"method": "single",
"channels": ["email"],
},
},
published=True,
)
print(put_tenant_template_response.id)package main
import (
"context"
"fmt"
"github.com/trycourier/courier-go/v4"
"github.com/trycourier/courier-go/v4/option"
"github.com/trycourier/courier-go/v4/shared"
)
func main() {
client := courier.NewClient(
option.WithAPIKey("My API Key"),
)
putTenantTemplateResponse, err := client.Tenants.Templates.Replace(
context.TODO(),
"template_id",
courier.TenantTemplateReplaceParams{
TenantID: "tenant_id",
PutTenantTemplateRequest: courier.PutTenantTemplateRequestParam{
Template: courier.TenantTemplateInputParam{
Content: shared.ElementalContentParam{
Elements: []shared.ElementalNodeUnionParam{{
OfElementalTextNodeWithType: &shared.ElementalTextNodeWithTypeParam{
ElementalTextNodeParam: shared.ElementalTextNodeParam{
ElementalBaseNodeParam: shared.ElementalBaseNodeParam{},
},
},
}},
Version: "2022-01-01",
},
},
},
},
)
if err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", putTenantTemplateResponse.ID)
}
package com.courier.example;
import com.courier.client.CourierClient;
import com.courier.client.okhttp.CourierOkHttpClient;
import com.courier.models.ElementalContent;
import com.courier.models.ElementalTextNodeWithType;
import com.courier.models.tenants.PutTenantTemplateRequest;
import com.courier.models.tenants.PutTenantTemplateResponse;
import com.courier.models.tenants.TenantTemplateInput;
import com.courier.models.tenants.templates.TemplateReplaceParams;
public final class Main {
private Main() {}
public static void main(String[] args) {
CourierClient client = CourierOkHttpClient.fromEnv();
TemplateReplaceParams params = TemplateReplaceParams.builder()
.tenantId("tenant_id")
.templateId("template_id")
.putTenantTemplateRequest(PutTenantTemplateRequest.builder()
.template(TenantTemplateInput.builder()
.content(ElementalContent.builder()
.addElement(ElementalTextNodeWithType.builder().build())
.version("2022-01-01")
.build())
.build())
.build())
.build();
PutTenantTemplateResponse putTenantTemplateResponse = client.tenants().templates().replace(params);
}
}require "courier"
courier = Courier::Client.new(api_key: "My API Key")
put_tenant_template_response = courier.tenants.templates.replace(
"template_id",
tenant_id: "tenant_id",
template: {content: {elements: [{}], version: "2022-01-01"}}
)
puts(put_tenant_template_response)<?php
require_once dirname(__DIR__) . '/vendor/autoload.php';
use Courier\Client;
use Courier\Core\Exceptions\APIException;
$client = new Client(apiKey: getenv('COURIER_API_KEY') ?: 'My API Key');
try {
$putTenantTemplateResponse = $client->tenants->templates->replace(
'template_id',
tenantID: 'tenant_id',
template: [
'content' => [
'elements' => [['type' => 'channel']], 'version' => '2022-01-01'
],
'channels' => [
'foo' => [
'brandID' => 'brand_id',
'if' => 'if',
'metadata' => [
'utm' => [
'campaign' => 'campaign',
'content' => 'content',
'medium' => 'medium',
'source' => 'source',
'term' => 'term',
],
],
'override' => ['foo' => 'bar'],
'providers' => ['string'],
'routingMethod' => 'all',
'timeouts' => ['channel' => 0, 'provider' => 0],
],
],
'providers' => [
'foo' => [
'if' => 'if',
'metadata' => [
'utm' => [
'campaign' => 'campaign',
'content' => 'content',
'medium' => 'medium',
'source' => 'source',
'term' => 'term',
],
],
'override' => ['foo' => 'bar'],
'timeouts' => 0,
],
],
'routing' => ['channels' => ['email'], 'method' => 'single'],
],
published: true,
);
var_dump($putTenantTemplateResponse);
} catch (APIException $e) {
echo $e->getMessage();
}using System;
using System.Collections.Generic;
using System.Text.Json;
using TryCourier;
using TryCourier.Models;
using TryCourier.Models.Tenants.Templates;
CourierClient client = new();
TemplateReplaceParams parameters = new()
{
TenantID = "tenant_id",
TemplateID = "template_id",
Template = new()
{
Content = new()
{
Elements =
[
new ElementalChannelNodeWithType()
{
Type = ElementalChannelNodeWithTypeIntersectionMember1Type.Channel,
},
],
Version = "2022-01-01",
},
Channels = new Dictionary<string, Channel>()
{
{ "foo", new()
{
BrandID = "brand_id",
If = "if",
Metadata = new()
{
Utm = new()
{
Campaign = "campaign",
Content = "content",
Medium = "medium",
Source = "source",
Term = "term",
},
},
Override = new Dictionary<string, JsonElement>()
{
{ "foo", JsonSerializer.SerializeToElement("bar") }
},
Providers =
[
"string"
],
RoutingMethod = RoutingMethod.All,
Timeouts = new()
{
Channel = 0,
Provider = 0,
},
} },
},
Providers = new Dictionary<string, MessageProvidersType>()
{
{ "foo", new()
{
If = "if",
Metadata = new()
{
Utm = new()
{
Campaign = "campaign",
Content = "content",
Medium = "medium",
Source = "source",
Term = "term",
},
},
Override = new Dictionary<string, JsonElement>()
{
{ "foo", JsonSerializer.SerializeToElement("bar") }
},
Timeouts = 0,
} },
},
Routing = new()
{
Channels =
[
"email"
],
Method = Method.Single,
},
},
};
var putTenantTemplateResponse = await client.Tenants.Templates.Replace(parameters);
Console.WriteLine(putTenantTemplateResponse);courier tenants:templates replace \
--api-key 'My API Key' \
--tenant-id tenant_id \
--template-id template_id \
--template "{content: {elements: [{}], version: '2022-01-01'}}"curl --request PUT \
--url https://api.courier.com/tenants/{tenant_id}/templates/{template_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"published": true,
"template": {
"content": {
"version": "2022-01-01",
"elements": [
{
"type": "channel",
"channel": "email",
"elements": [
{
"type": "text",
"content": "Hello {{name}}"
}
]
}
]
},
"routing": {
"method": "single",
"channels": [
"email"
]
}
}
}
'{
"id": "tenant_abc",
"version": "string",
"published_at": "2024-01-15T10:30:00.000Z"
}{
"id": "tenant_abc",
"version": "string",
"published_at": "2024-01-15T10:30:00.000Z"
}{
"message": "Template content must place its elements inside a channel block, e.g. { \"type\": \"channel\", \"channel\": \"inbox\", \"elements\": [...] }. Content without one is stored but cannot be opened in the template designer.",
"type": "invalid_request_error"
}{
"message": "Example message text",
"type": "invalid_request_error"
}{
"message": "Example message text",
"type": "invalid_request_error"
}Create or update a Tenant Template
Creates or updates a notification template scoped to one tenant, letting a tenant override the content the workspace template would send.
This is an upsert: it creates when the tenant has no template under template_id, and updates when it does. On the create half, content must place its elements inside a channel block — { "type": "channel", "channel": "email", "elements": [...] } — or the request returns 400. The template designer renders only the channel block matching the tab it draws, so content stored without one cannot be opened. An empty elements array is accepted, as is the { title, body } shorthand, which has no elements to wrap. Updates are not checked, so tenant templates already stored without a wrapper stay editable.
import Courier from '@trycourier/courier';
const client = new Courier({
apiKey: process.env['COURIER_API_KEY'], // This is the default and can be omitted
});
const putTenantTemplateResponse = await client.tenants.templates.replace('template_id', {
tenant_id: 'tenant_id',
template: {
content: { version: '2022-01-01', elements: [{ type: 'channel' }] },
routing: { method: 'single', channels: ['email'] },
},
published: true,
});
console.log(putTenantTemplateResponse.id);import os
from courier import Courier
client = Courier(
api_key=os.environ.get("COURIER_API_KEY"), # This is the default and can be omitted
)
put_tenant_template_response = client.tenants.templates.replace(
template_id="template_id",
tenant_id="tenant_id",
template={
"content": {
"version": "2022-01-01",
"elements": [{
"type": "channel"
}],
},
"routing": {
"method": "single",
"channels": ["email"],
},
},
published=True,
)
print(put_tenant_template_response.id)package main
import (
"context"
"fmt"
"github.com/trycourier/courier-go/v4"
"github.com/trycourier/courier-go/v4/option"
"github.com/trycourier/courier-go/v4/shared"
)
func main() {
client := courier.NewClient(
option.WithAPIKey("My API Key"),
)
putTenantTemplateResponse, err := client.Tenants.Templates.Replace(
context.TODO(),
"template_id",
courier.TenantTemplateReplaceParams{
TenantID: "tenant_id",
PutTenantTemplateRequest: courier.PutTenantTemplateRequestParam{
Template: courier.TenantTemplateInputParam{
Content: shared.ElementalContentParam{
Elements: []shared.ElementalNodeUnionParam{{
OfElementalTextNodeWithType: &shared.ElementalTextNodeWithTypeParam{
ElementalTextNodeParam: shared.ElementalTextNodeParam{
ElementalBaseNodeParam: shared.ElementalBaseNodeParam{},
},
},
}},
Version: "2022-01-01",
},
},
},
},
)
if err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", putTenantTemplateResponse.ID)
}
package com.courier.example;
import com.courier.client.CourierClient;
import com.courier.client.okhttp.CourierOkHttpClient;
import com.courier.models.ElementalContent;
import com.courier.models.ElementalTextNodeWithType;
import com.courier.models.tenants.PutTenantTemplateRequest;
import com.courier.models.tenants.PutTenantTemplateResponse;
import com.courier.models.tenants.TenantTemplateInput;
import com.courier.models.tenants.templates.TemplateReplaceParams;
public final class Main {
private Main() {}
public static void main(String[] args) {
CourierClient client = CourierOkHttpClient.fromEnv();
TemplateReplaceParams params = TemplateReplaceParams.builder()
.tenantId("tenant_id")
.templateId("template_id")
.putTenantTemplateRequest(PutTenantTemplateRequest.builder()
.template(TenantTemplateInput.builder()
.content(ElementalContent.builder()
.addElement(ElementalTextNodeWithType.builder().build())
.version("2022-01-01")
.build())
.build())
.build())
.build();
PutTenantTemplateResponse putTenantTemplateResponse = client.tenants().templates().replace(params);
}
}require "courier"
courier = Courier::Client.new(api_key: "My API Key")
put_tenant_template_response = courier.tenants.templates.replace(
"template_id",
tenant_id: "tenant_id",
template: {content: {elements: [{}], version: "2022-01-01"}}
)
puts(put_tenant_template_response)<?php
require_once dirname(__DIR__) . '/vendor/autoload.php';
use Courier\Client;
use Courier\Core\Exceptions\APIException;
$client = new Client(apiKey: getenv('COURIER_API_KEY') ?: 'My API Key');
try {
$putTenantTemplateResponse = $client->tenants->templates->replace(
'template_id',
tenantID: 'tenant_id',
template: [
'content' => [
'elements' => [['type' => 'channel']], 'version' => '2022-01-01'
],
'channels' => [
'foo' => [
'brandID' => 'brand_id',
'if' => 'if',
'metadata' => [
'utm' => [
'campaign' => 'campaign',
'content' => 'content',
'medium' => 'medium',
'source' => 'source',
'term' => 'term',
],
],
'override' => ['foo' => 'bar'],
'providers' => ['string'],
'routingMethod' => 'all',
'timeouts' => ['channel' => 0, 'provider' => 0],
],
],
'providers' => [
'foo' => [
'if' => 'if',
'metadata' => [
'utm' => [
'campaign' => 'campaign',
'content' => 'content',
'medium' => 'medium',
'source' => 'source',
'term' => 'term',
],
],
'override' => ['foo' => 'bar'],
'timeouts' => 0,
],
],
'routing' => ['channels' => ['email'], 'method' => 'single'],
],
published: true,
);
var_dump($putTenantTemplateResponse);
} catch (APIException $e) {
echo $e->getMessage();
}using System;
using System.Collections.Generic;
using System.Text.Json;
using TryCourier;
using TryCourier.Models;
using TryCourier.Models.Tenants.Templates;
CourierClient client = new();
TemplateReplaceParams parameters = new()
{
TenantID = "tenant_id",
TemplateID = "template_id",
Template = new()
{
Content = new()
{
Elements =
[
new ElementalChannelNodeWithType()
{
Type = ElementalChannelNodeWithTypeIntersectionMember1Type.Channel,
},
],
Version = "2022-01-01",
},
Channels = new Dictionary<string, Channel>()
{
{ "foo", new()
{
BrandID = "brand_id",
If = "if",
Metadata = new()
{
Utm = new()
{
Campaign = "campaign",
Content = "content",
Medium = "medium",
Source = "source",
Term = "term",
},
},
Override = new Dictionary<string, JsonElement>()
{
{ "foo", JsonSerializer.SerializeToElement("bar") }
},
Providers =
[
"string"
],
RoutingMethod = RoutingMethod.All,
Timeouts = new()
{
Channel = 0,
Provider = 0,
},
} },
},
Providers = new Dictionary<string, MessageProvidersType>()
{
{ "foo", new()
{
If = "if",
Metadata = new()
{
Utm = new()
{
Campaign = "campaign",
Content = "content",
Medium = "medium",
Source = "source",
Term = "term",
},
},
Override = new Dictionary<string, JsonElement>()
{
{ "foo", JsonSerializer.SerializeToElement("bar") }
},
Timeouts = 0,
} },
},
Routing = new()
{
Channels =
[
"email"
],
Method = Method.Single,
},
},
};
var putTenantTemplateResponse = await client.Tenants.Templates.Replace(parameters);
Console.WriteLine(putTenantTemplateResponse);courier tenants:templates replace \
--api-key 'My API Key' \
--tenant-id tenant_id \
--template-id template_id \
--template "{content: {elements: [{}], version: '2022-01-01'}}"curl --request PUT \
--url https://api.courier.com/tenants/{tenant_id}/templates/{template_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"published": true,
"template": {
"content": {
"version": "2022-01-01",
"elements": [
{
"type": "channel",
"channel": "email",
"elements": [
{
"type": "text",
"content": "Hello {{name}}"
}
]
}
]
},
"routing": {
"method": "single",
"channels": [
"email"
]
}
}
}
'{
"id": "tenant_abc",
"version": "string",
"published_at": "2024-01-15T10:30:00.000Z"
}{
"id": "tenant_abc",
"version": "string",
"published_at": "2024-01-15T10:30:00.000Z"
}{
"message": "Template content must place its elements inside a channel block, e.g. { \"type\": \"channel\", \"channel\": \"inbox\", \"elements\": [...] }. Content without one is stored but cannot be opened in the template designer.",
"type": "invalid_request_error"
}{
"message": "Example message text",
"type": "invalid_request_error"
}{
"message": "Example message text",
"type": "invalid_request_error"
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Id of the tenant for which to create or update the template.
Id of the template to be created or updated.
Body
Request body for creating or updating a tenant notification template
Template configuration for creating or updating a tenant notification template
Show child attributes
Show child attributes
Whether to publish the template immediately after saving. When true, the template becomes the active/published version. When false (default), the template is saved as a draft.
Response
Template updated successfully