Prerequisites
- A Viber bot account
- Your Viber bot auth token
- A webhook server that receives Viber POST requests
Setup
Open the in Courier, enter your auth token and sender name, then save.Webhooks server
Viber requires a registered webhooks server before you send notifications:curl -X POST 'https://chatapi.viber.com/pa/set_webhook' \
-H 'Content-Type: application/json' \
-H 'X-Viber-Auth-Token: VIBER_AUTH_TOKEN' \
-d '{ "url": "YOUR_WEBHOOK_SERVER_URL", "event_types": ["delivered"] }'
Profile requirements
Viber addresses the recipient byUserID, which goes in viber.receiver on the profile. Store it once with , which merges into the profile and creates it if it does not exist:
const profile = await client.profiles.create('user_123', {
profile: {
viber: {
receiver: '12943673==',
},
},
});
profile = client.profiles.create(
user_id="user_123",
profile={
"viber": {
"receiver": "12943673==",
},
},
)
curl --request POST \
--url https://api.courier.com/profiles/user_123 \
--header "Authorization: Bearer $COURIER_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"profile": {
"viber": {
"receiver": "12943673=="
}
}
}'
profile = courier.profiles.create(
"user_123",
profile: {
viber: {
receiver: "12943673=="
}
}
)
profile, err := client.Profiles.New(
context.TODO(),
"user_123",
courier.ProfileNewParams{
Profile: map[string]any{
"viber": map[string]any{
"receiver": "12943673==",
},
},
},
)
ProfileCreateParams params = ProfileCreateParams.builder()
.userId("user_123")
.profile(ProfileCreateParams.Profile.builder()
.putAdditionalProperty("viber", JsonValue.from(java.util.Map.of(
"receiver", "12943673==")))
.build())
.build();
ProfileCreateResponse profile = client.profiles().create(params);
$profile = $client->profiles->create('user_123', profile: [
'viber' => [
'receiver' => '12943673==',
],
]);
ProfileCreateParams parameters = new()
{
UserID = "user_123",
Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"viber": {
"receiver": "12943673=="
}
}
"""
),
};
var profile = await client.Profiles.Create(parameters);
courier profiles create \
--api-key "$COURIER_API_KEY" \
--user-id user_123 \
--profile '{"viber":{"receiver":"12943673=="}}'
With Courier MCP, save the Viber receiver 12943673== on user_123.
UserID to your webhooks server after a user messages the Viber bot.
Then send to user_id and Courier resolves the address.
For a one-off with no stored profile, pass it inline instead: "to": { "viber": { "receiver": "12943673==" } }.
Send by user id
The call in every language, and the rest of the profile object.
Send to a recipient
- Send to user id
- Send to `viber`
Courier reads
viber off the saved profile, so preferences apply and the value can change without touching this code.const { requestId } = await courier.send.message({
message: {
to: {
user_id: "user_123",
},
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
},
});
response = client.send.message(
message={
"to": {
"user_id": "user_123",
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
},
)
curl -X POST https://api.courier.com/send \
-H "Authorization: Bearer $COURIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": {
"to": {
"user_id": "user_123"
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40"
}
}'
response = courier.send_.message(
message: {
to: {
user_id: "user_123"
},
template: "nt_01kx4h2jdafq8bk9aftxak4b40"
}
)
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{
OfUserRecipient: &shared.UserRecipientParam{
UserID: courier.String("user_123"),
},
},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
},
})
SendMessageParams params = SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
.to(UserRecipient.builder().userId("user_123").build())
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.build())
.build();
SendMessageResponse response = client.send().message(params);
$response = $client->send->message(
message: [
'to' => [
'user_id' => 'user_123',
],
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
],
);
SendMessageParams parameters = new()
{
Message = new()
{
To = new UserRecipient { UserID = "user_123" },
Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
},
};
var response = await client.Send.Message(parameters);
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"user_id": "user_123"}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40
With Courier MCP, send my template to user_123 by email.
Pass it inline instead and nothing is stored. Swap this
to object into the call on the other tab.const { requestId } = await courier.send.message({
message: {
// A provider recipient is a profile field, so it sits outside the typed union.
to: {
viber: {
receiver: "12943673==",
},
} as any,
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
},
});
response = client.send.message(
message={
"to": {
"viber": {
"receiver": "12943673==",
},
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
},
)
curl -X POST https://api.courier.com/send \
-H "Authorization: Bearer $COURIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": {
"to": {
"viber": {
"receiver": "12943673=="
}
},
"template": "nt_01kx4h2jdafq8bk9aftxak4b40"
}
}'
response = courier.send_.message(
message: {
to: {
viber: {
receiver: "12943673=="
}
},
template: "nt_01kx4h2jdafq8bk9aftxak4b40"
}
)
// A provider recipient is a profile field, so it sits outside the typed union.
to := param.Override[shared.UserRecipientParam](
json.RawMessage(`{"viber": {"receiver": "12943673=="}}`),
)
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
},
})
// A provider recipient is a profile field, so it sits outside the typed union.
UserRecipient to = UserRecipient.builder()
.putAdditionalProperty("viber", JsonValue.from(Map.of(
"receiver", "12943673==")))
.build();
client.send().message(SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
.to(to)
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.build())
.build());
$response = $client->send->message(
message: [
'to' => [
'viber' => [
'receiver' => '12943673==',
],
],
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
],
);
// A provider recipient is a profile field, so it sits outside the typed union.
UserRecipient to = UserRecipient.FromRawUnchecked(
JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""{"viber": {"receiver": "12943673=="}}"""
)
);
var response = await client.Send.Message(new() { Message = new() { To = to, Template = "nt_01kx4h2jdafq8bk9aftxak4b40" } });
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"viber": {"receiver": "12943673=="}}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40
With Courier MCP, send my template to the Viber receiver 12943673==.
Overrides
covers the two levels and which one wins. A provider override changes the request body Courier sends to Viber, or swaps the auth token, sender name, and API URL.Body overrides
Override the request body that Courier sends to the Viber Send Message API.const { requestId } = await courier.send.message({
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
viber: {
receiver: "12943673==",
},
},
providers: {
viber: {
override: {
body: {
type: "picture",
media: "https://example.com/image.jpg",
},
},
},
},
},
});
response = client.send.message(
message={
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"to": {
"viber": {
"receiver": "12943673==",
},
},
"providers": {
"viber": {
"override": {
"body": {
"type": "picture",
"media": "https://example.com/image.jpg",
},
},
},
},
},
)
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": {
"viber": {
"receiver": "12943673=="
}
},
"providers": {
"viber": {
"override": {
"body": {
"type": "picture",
"media": "https://example.com/image.jpg"
}
}
}
}
}
}'
response = courier.send_.message(
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
viber: {
receiver: "12943673=="
}
},
providers: {
viber: {
override: {
body: {
type: "picture",
media: "https://example.com/image.jpg"
}
}
}
}
}
)
// This provider addresses the recipient with fields outside the typed
// UserRecipient model, so pass the recipient as raw JSON.
to := param.Override[shared.UserRecipientParam](json.RawMessage(`{
"viber": {
"receiver": "12943673=="
}
}`))
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
Providers: shared.MessageProvidersParam{
"viber": shared.MessageProvidersTypeParam{
Override: map[string]any{
"body": map[string]any{
"type": "picture",
"media": "https://example.com/image.jpg",
},
},
},
},
},
})
SendMessageParams params = SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
// This provider addresses the recipient with fields outside the
// typed UserRecipient model, so pass them as additional properties.
.to(UserRecipient.builder()
.putAdditionalProperty("viber", JsonValue.from(java.util.Map.of(
"receiver", "12943673=="
)))
.build())
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.providers(MessageProviders.builder()
.putAdditionalProperty("viber", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
"body", java.util.Map.of(
"type", "picture",
"media", "https://example.com/image.jpg"
)
))))
.build())
.build())
.build();
SendMessageResponse response = client.send().message(params);
$response = $client->send->message(
message: [
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
'to' => [
'viber' => [
'receiver' => '12943673==',
],
],
'providers' => [
'viber' => [
'override' => [
'body' => [
'type' => 'picture',
'media' => 'https://example.com/image.jpg',
],
],
],
],
],
);
SendMessageParams parameters = new()
{
Message = new()
{
// This provider addresses the recipient with fields outside the
// typed UserRecipient model, so build it from raw JSON.
To = UserRecipient.FromRawUnchecked(
JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"viber": {
"receiver": "12943673=="
}
}
"""
)
),
Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
Providers = new Dictionary<string, MessageProvidersType>()
{
{
"viber",
new()
{
Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"body": {
"type": "picture",
"media": "https://example.com/image.jpg"
}
}
"""
),
}
},
},
},
};
var response = await client.Send.Message(parameters);
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"viber": {"receiver": "12943673=="}}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
--message.providers '{"viber": {"override": {"body": {"type": "picture", "media": "https://example.com/image.jpg"}}}}'
With Courier MCP, send my template to this Viber receiver and override the message body.
Config overrides
You can swap the auth token, sender name, or API URL at send time:const { requestId } = await courier.send.message({
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
viber: {
receiver: "12943673==",
},
},
providers: {
viber: {
override: {
config: {
token: "RUNTIME_AUTH_TOKEN",
name: "My Bot Name",
},
},
},
},
},
});
response = client.send.message(
message={
"template": "nt_01kx4h2jdafq8bk9aftxak4b40",
"to": {
"viber": {
"receiver": "12943673==",
},
},
"providers": {
"viber": {
"override": {
"config": {
"token": "RUNTIME_AUTH_TOKEN",
"name": "My Bot Name",
},
},
},
},
},
)
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": {
"viber": {
"receiver": "12943673=="
}
},
"providers": {
"viber": {
"override": {
"config": {
"token": "RUNTIME_AUTH_TOKEN",
"name": "My Bot Name"
}
}
}
}
}
}'
response = courier.send_.message(
message: {
template: "nt_01kx4h2jdafq8bk9aftxak4b40",
to: {
viber: {
receiver: "12943673=="
}
},
providers: {
viber: {
override: {
config: {
token: "RUNTIME_AUTH_TOKEN",
name: "My Bot Name"
}
}
}
}
}
)
// This provider addresses the recipient with fields outside the typed
// UserRecipient model, so pass the recipient as raw JSON.
to := param.Override[shared.UserRecipientParam](json.RawMessage(`{
"viber": {
"receiver": "12943673=="
}
}`))
response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
Message: courier.SendMessageParamsMessage{
To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
Providers: shared.MessageProvidersParam{
"viber": shared.MessageProvidersTypeParam{
Override: map[string]any{
"config": map[string]any{
"token": "RUNTIME_AUTH_TOKEN",
"name": "My Bot Name",
},
},
},
},
},
})
SendMessageParams params = SendMessageParams.builder()
.message(SendMessageParams.Message.builder()
// This provider addresses the recipient with fields outside the
// typed UserRecipient model, so pass them as additional properties.
.to(UserRecipient.builder()
.putAdditionalProperty("viber", JsonValue.from(java.util.Map.of(
"receiver", "12943673=="
)))
.build())
.template("nt_01kx4h2jdafq8bk9aftxak4b40")
.providers(MessageProviders.builder()
.putAdditionalProperty("viber", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
"config", java.util.Map.of(
"token", "RUNTIME_AUTH_TOKEN",
"name", "My Bot Name"
)
))))
.build())
.build())
.build();
SendMessageResponse response = client.send().message(params);
$response = $client->send->message(
message: [
'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
'to' => [
'viber' => [
'receiver' => '12943673==',
],
],
'providers' => [
'viber' => [
'override' => [
'config' => [
'token' => 'RUNTIME_AUTH_TOKEN',
'name' => 'My Bot Name',
],
],
],
],
],
);
SendMessageParams parameters = new()
{
Message = new()
{
// This provider addresses the recipient with fields outside the
// typed UserRecipient model, so build it from raw JSON.
To = UserRecipient.FromRawUnchecked(
JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"viber": {
"receiver": "12943673=="
}
}
"""
)
),
Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
Providers = new Dictionary<string, MessageProvidersType>()
{
{
"viber",
new()
{
Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
"""
{
"config": {
"token": "RUNTIME_AUTH_TOKEN",
"name": "My Bot Name"
}
}
"""
),
}
},
},
},
};
var response = await client.Send.Message(parameters);
courier send message \
--api-key "$COURIER_API_KEY" \
--message.to '{"viber": {"receiver": "12943673=="}}' \
--message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
--message.providers '{"viber": {"override": {"config": {"token": "RUNTIME_AUTH_TOKEN", "name": "My Bot Name"}}}}'
With Courier MCP, send my template to this Viber receiver with a different auth token.
Provider details
viber
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.