> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Send Stream Chat messages through Courier

> Connect Stream Chat with an API key and secret, and target a channel from the profile.

export const Endpoint = ({method, path, name, href, children, bare}) => {
  const verb = String(method || "").toUpperCase();
  const title = verb + " " + path;
  const label = children || name || path;
  if (bare) {
    return href ? <a href={href}><code>{title}</code></a> : <code>{title}</code>;
  }
  if (!href) {
    return <span className="cx-endpoint" data-method={verb} title={title}>
        <span className="cx-endpoint-label">{label}</span>
        <span className="cx-endpoint-method">{verb}</span>
      </span>;
  }
  return <a className="cx-endpoint" data-method={verb} href={href} title={title}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">{verb}</span>
    </a>;
};

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

## Prerequisites

* [A Stream Chat account with a project](https://getstream.io/chat/)
* [Your API key, API secret, and sender ID](https://dashboard.getstream.io/)

## Setup

<Steps>
  <Step title="Get your Stream Chat credentials">
    In Stream, open the [dashboard](https://dashboard.getstream.io/), select your app, and copy the API key and API secret.
  </Step>

  <Step title="Configure in Courier">
    Open the <AppLink href="https://app.courier.com/integrations/catalog/stream-chat">Stream Chat integration</AppLink> in Courier, enter your API key, API secret, and sender ID, then save.
  </Step>
</Steps>

## Profile requirements

Stream Chat posts a new message to a channel, or updates one that already exists. The profile you send to needs a `streamChat` object for whichever you want. Store it once with <Endpoint method="POST" path="/profiles/{user_id}" name="Create a Profile" href="/docs/api-reference/user-profiles/create-a-profile" />, which merges into the profile and creates it if it does not exist:

<Tabs>
  <Tab title="New message">
    `channelType` and `channelId` name the channel to post into.

    <CodeGroup>
      ```javascript Node.js highlight={3-6} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          streamChat: {
            channelType: 'messaging',
            channelId: 'my-channel-id',
          },
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "streamChat": {
                  "channelType": "messaging",
                  "channelId": "my-channel-id",
              },
          },
      )
      ```

      ```bash cURL highlight={7-10} theme={null}
      curl --request POST \
        --url https://api.courier.com/profiles/user_123 \
        --header "Authorization: Bearer $COURIER_API_KEY" \
        --header 'Content-Type: application/json' \
        --data '{
          "profile": {
            "streamChat": {
              "channelType": "messaging",
              "channelId": "my-channel-id"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          streamChat: {
            channelType: "messaging",
            channelId: "my-channel-id"
          }
        }
      )
      ```

      ```go Go highlight={6-9} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"streamChat": map[string]any{
      				"channelType": "messaging",
      				"channelId": "my-channel-id",
      			},
      		},
      	},
      )
      ```

      ```java Java highlight={4-6} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("streamChat", JsonValue.from(java.util.Map.of(
                  "channelType", "messaging",
                  "channelId", "my-channel-id")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2-5} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'streamChat' => [
          'channelType' => 'messaging',
          'channelId' => 'my-channel-id',
        ],
      ]);
      ```

      ```csharp C# highlight={7-10} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "streamChat": {
                  "channelType": "messaging",
                  "channelId": "my-channel-id"
                }
              }
              """
          ),
      };
      var profile = await client.Profiles.Create(parameters);
      ```

      ```bash CLI highlight={4} wrap theme={null}
      courier profiles create \
        --api-key "$COURIER_API_KEY" \
        --user-id user_123 \
        --profile '{"streamChat":{"channelType":"messaging","channelId":"my-channel-id"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the Stream Chat messaging channel my-channel-id on user_123.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Update a message">
    `messageId` replaces the content of a message already posted.

    <CodeGroup>
      ```javascript Node.js highlight={3-5} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          streamChat: {
            messageId: 'my-message-id',
          },
        },
      });
      ```

      ```python Python highlight={4-6} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "streamChat": {
                  "messageId": "my-message-id",
              },
          },
      )
      ```

      ```bash cURL highlight={7-9} theme={null}
      curl --request POST \
        --url https://api.courier.com/profiles/user_123 \
        --header "Authorization: Bearer $COURIER_API_KEY" \
        --header 'Content-Type: application/json' \
        --data '{
          "profile": {
            "streamChat": {
              "messageId": "my-message-id"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={4-6} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          streamChat: {
            messageId: "my-message-id"
          }
        }
      )
      ```

      ```go Go highlight={6-8} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"streamChat": map[string]any{
      				"messageId": "my-message-id",
      			},
      		},
      	},
      )
      ```

      ```java Java highlight={4-5} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("streamChat", JsonValue.from(java.util.Map.of(
                  "messageId", "my-message-id")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2-4} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'streamChat' => [
          'messageId' => 'my-message-id',
        ],
      ]);
      ```

      ```csharp C# highlight={7-9} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "streamChat": {
                  "messageId": "my-message-id"
                }
              }
              """
          ),
      };
      var profile = await client.Profiles.Create(parameters);
      ```

      ```bash CLI highlight={4} wrap theme={null}
      courier profiles create \
        --api-key "$COURIER_API_KEY" \
        --user-id user_123 \
        --profile '{"streamChat":{"messageId":"my-message-id"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the Stream Chat message id my-message-id on user_123.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

Then send to `user_id` and Courier resolves the address.

For a one-off with no stored profile, pass it inline instead: `"to": { "streamChat": { "channelType": "messaging", "channelId": "my-channel-id" } }`.

<CardGroup cols={1}>
  <Card title="Send by user id" icon="user" href="/docs/recipients/overview#send-by-user-id">
    The call in every language, and the rest of the profile object.
  </Card>
</CardGroup>

## Send to a recipient

<Tabs>
  <Tab title="Send to user id">
    Courier reads `streamChat` off the saved profile, so preferences apply and the value can change without touching this code.

    <CodeGroup>
      ```javascript Node.js highlight={4} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
        },
      });
      ```

      ```python Python highlight={4} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      )
      ```

      ```bash cURL highlight={7} wrap theme={null}
      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"
          }
        }'
      ```

      ```ruby Ruby highlight={4} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40"
        }
      )
      ```

      ```go Go highlight={5} theme={null}
      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"),
      	},
      })
      ```

      ```java Java highlight={3} theme={null}
      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);
      ```

      ```php PHP highlight={4} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
        ],
      );
      ```

      ```csharp C# highlight={5} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      };

      var response = await client.Send.Message(parameters);
      ```

      ```bash CLI highlight={3} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"user_id": "user_123"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40
      ```

      ```text MCP theme={null}
      With Courier MCP, send my template to user_123 by email.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Send to `streamChat`">
    Pass it inline instead and nothing is stored. Swap this `to` object into the call on the other tab.

    <CodeGroup>
      ```javascript Node.js highlight={5-8} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          // A provider recipient is a profile field, so it sits outside the typed union.
          to: {
            streamChat: {
              channelType: "messaging",
              channelId: "my-channel-id",
            },
          } as any,
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "streamChat": {
                      "channelType": "messaging",
                      "channelId": "my-channel-id",
                  },
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          },
      )
      ```

      ```bash cURL highlight={7-10} wrap theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer $COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": {
              "streamChat": {
                "channelType": "messaging",
                "channelId": "my-channel-id"
              }
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40"
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            streamChat: {
              channelType: "messaging",
              channelId: "my-channel-id"
            }
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40"
        }
      )
      ```

      ```go Go highlight={2-4} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      to := param.Override[shared.UserRecipientParam](
      	json.RawMessage(`{"streamChat": {"channelType": "messaging", "channelId": "my-channel-id"}}`),
      )

      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      	},
      })
      ```

      ```java Java highlight={3-5} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      UserRecipient to = UserRecipient.builder()
          .putAdditionalProperty("streamChat", JsonValue.from(Map.of(
                  "channelType", "messaging",
                  "channelId", "my-channel-id")))
          .build();

      client.send().message(SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(to)
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .build())
          .build());
      ```

      ```php PHP highlight={4-7} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'streamChat' => [
              'channelType' => 'messaging',
              'channelId' => 'my-channel-id',
            ],
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
        ],
      );
      ```

      ```csharp C# highlight={3-5} theme={null}
      // A provider recipient is a profile field, so it sits outside the typed union.
      UserRecipient to = UserRecipient.FromRawUnchecked(
          JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """{"streamChat": {"channelType": "messaging", "channelId": "my-channel-id"}}"""
          )
      );

      var response = await client.Send.Message(new() { Message = new() { To = to, Template = "nt_01kx4h2jdafq8bk9aftxak4b40" } });
      ```

      ```bash CLI highlight={3} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"streamChat": {"channelType": "messaging", "channelId": "my-channel-id"}}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40
      ```

      ```text MCP theme={null}
      With Courier MCP, send my template to the Stream Chat channel my-channel-id.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Overrides

<Doc href="/docs/send/overrides#how-overrides-work">How overrides work</Doc> covers the two levels and which one wins.

A provider override changes the request body, the API credentials, and the endpoint URL Courier sends to Stream Chat.

| Field    | Sets                                                                                                                                                              |
| :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `body`   | Any field Stream Chat's [SendMessage endpoint](https://getstream.io/chat/docs/rest/#/product%3Achat/SendMessage) accepts at `POST /channels/{type}/{id}/message`. |
| `config` | `apiKey`, `apiSecret`, `senderId`, and `baseUrl`, which defaults to `https://chat-us-east-1.stream-io-api.com`.                                                   |

<CodeGroup>
  ```javascript Node.js highlight={14-26} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      to: {
        streamChat: {
          channelType: "messaging",
          channelId: "my-channel-id",
        },
      },
      data: {
        name: "Sarah Bennett",
      },
      providers: {
        "stream-chat": {
          override: {
            body: {
              skip_push: true,
            },
            config: {
              baseUrl: "https://custom-stream-endpoint.example.com",
              apiKey: "RUNTIME_API_KEY",
              apiSecret: "RUNTIME_API_SECRET",
              senderId: "RUNTIME_SENDER_ID",
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={14-26} theme={null}
  response = client.send.message(
      message={
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "to": {
              "streamChat": {
                  "channelType": "messaging",
                  "channelId": "my-channel-id",
              },
          },
          "data": {
              "name": "Sarah Bennett",
          },
          "providers": {
              "stream-chat": {
                  "override": {
                      "body": {
                          "skip_push": True,
                      },
                      "config": {
                          "baseUrl": "https://custom-stream-endpoint.example.com",
                          "apiKey": "RUNTIME_API_KEY",
                          "apiSecret": "RUNTIME_API_SECRET",
                          "senderId": "RUNTIME_SENDER_ID",
                      },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={17-29} wrap theme={null}
  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": {
          "streamChat": {
            "channelType": "messaging",
            "channelId": "my-channel-id"
          }
        },
        "data": {
          "name": "Sarah Bennett"
        },
        "providers": {
          "stream-chat": {
            "override": {
              "body": {
                "skip_push": true
              },
              "config": {
                "baseUrl": "https://custom-stream-endpoint.example.com",
                "apiKey": "RUNTIME_API_KEY",
                "apiSecret": "RUNTIME_API_SECRET",
                "senderId": "RUNTIME_SENDER_ID"
              }
            }
          }
        }
      }
    }'
  ```

  ```ruby Ruby highlight={14-26} theme={null}
  response = courier.send_.message(
    message: {
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      to: {
        streamChat: {
          channelType: "messaging",
          channelId: "my-channel-id"
        }
      },
      data: {
        name: "Sarah Bennett"
      },
      providers: {
        "stream-chat": {
          override: {
            body: {
              skip_push: true
            },
            config: {
              baseUrl: "https://custom-stream-endpoint.example.com",
              apiKey: "RUNTIME_API_KEY",
              apiSecret: "RUNTIME_API_SECRET",
              senderId: "RUNTIME_SENDER_ID"
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={18-30} theme={null}
  // 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(`{
    "streamChat": {
      "channelType": "messaging",
      "channelId": "my-channel-id"
    }
  }`))

  response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
  	Message: courier.SendMessageParamsMessage{
  		To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
  		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
  		Data: map[string]any{
  			"name": "Sarah Bennett",
  		},
  		Providers: shared.MessageProvidersParam{
  			"stream-chat": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"skip_push": true,
  					},
  					"config": map[string]any{
  						"baseUrl": "https://custom-stream-endpoint.example.com",
  						"apiKey": "RUNTIME_API_KEY",
  						"apiSecret": "RUNTIME_API_SECRET",
  						"senderId": "RUNTIME_SENDER_ID",
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={16-26} theme={null}
  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("streamChat", JsonValue.from(java.util.Map.of(
                  "channelType", "messaging",
                  "channelId", "my-channel-id"
              )))
              .build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .data(JsonValue.from(java.util.Map.of(
              "name", "Sarah Bennett"
          )))
          .providers(MessageProviders.builder()
              .putAdditionalProperty("stream-chat", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
                      "body", java.util.Map.of(
                          "skip_push", true
                      ),
                      "config", java.util.Map.of(
                          "baseUrl", "https://custom-stream-endpoint.example.com",
                          "apiKey", "RUNTIME_API_KEY",
                          "apiSecret", "RUNTIME_API_SECRET",
                          "senderId", "RUNTIME_SENDER_ID"
                      )
                  ))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={14-26} theme={null}
  $response = $client->send->message(
    message: [
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'to' => [
        'streamChat' => [
          'channelType' => 'messaging',
          'channelId' => 'my-channel-id',
        ],
      ],
      'data' => [
        'name' => 'Sarah Bennett',
      ],
      'providers' => [
        'stream-chat' => [
          'override' => [
            'body' => [
              'skip_push' => true,
            ],
            'config' => [
              'baseUrl' => 'https://custom-stream-endpoint.example.com',
              'apiKey' => 'RUNTIME_API_KEY',
              'apiSecret' => 'RUNTIME_API_SECRET',
              'senderId' => 'RUNTIME_SENDER_ID',
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={26-46} theme={null}
  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>>(
                  """
                  {
                    "streamChat": {
                      "channelType": "messaging",
                      "channelId": "my-channel-id"
                    }
                  }
                  """
              )
          ),
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Data = new Dictionary<string, JsonElement>()
          {
              { "name", JsonSerializer.SerializeToElement("Sarah Bennett") },
          },
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "stream-chat",
                  new()
                  {
                      Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                          """
                          {
                            "body": {
                              "skip_push": true
                            },
                            "config": {
                              "baseUrl": "https://custom-stream-endpoint.example.com",
                              "apiKey": "RUNTIME_API_KEY",
                              "apiSecret": "RUNTIME_API_SECRET",
                              "senderId": "RUNTIME_SENDER_ID"
                            }
                          }
                          """
                      ),
                  }
              },
          },
      },
  };

  var response = await client.Send.Message(parameters);
  ```

  ```bash CLI highlight={6} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"streamChat": {"channelType": "messaging", "channelId": "my-channel-id"}}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.data '{"name": "Sarah Bennett"}' \
    --message.providers '{"stream-chat": {"override": {"body": {"skip_push": true}, "config": {"baseUrl": "https://custom-stream-endpoint.example.com", "apiKey": "RUNTIME_API_KEY", "apiSecret": "RUNTIME_API_SECRET", "senderId": "RUNTIME_SENDER_ID"}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to this Stream Chat channel with a different API key and sender.
  ```
</CodeGroup>

## Provider details

```text theme={null}
stream-chat
```

Courier recommends routing to the channel. Naming this key in `routing.channels` instead is supported, and sends through just this provider.

<Card title="Send to a specific provider" icon="bullseye-arrow" href="/docs/send/send-to-a-provider" horizontal arrow="true">
  When that is worth doing, and what you give up: failover, channel priority, and providers you add later.
</Card>
