> ## 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 Slack messages through Courier

> Connect a Slack app with a bot token and target users by email or ID, or channels, with Block Kit.

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

* <AppLink href="https://app.courier.com/signup">A Courier account</AppLink>
* [A Slack account](https://slack.com/get-started)

## Setup

<Note>
  * <Doc href="/docs/reference/api-overview">Courier API Reference</Doc>
  * [Slack API Documentation](https://api.slack.com/)
  * [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/)
</Note>

<Steps>
  <Step title="Add the Slack integration in Courier">
    Go to the <AppLink href="https://app.courier.com/integrations">Integrations page</AppLink> and select Slack. Click "Install".

    <Frame caption="Courier Slack Integration">
      <img src="https://mintcdn.com/courier-4f1f25dc/rYENcCCTyPPrDtw0/assets/slack-integration.webp?fit=max&auto=format&n=rYENcCCTyPPrDtw0&q=85&s=4e0cd39a1f8e3dde42d023e93c6d8716" alt="" width="1488" height="1622" data-path="assets/slack-integration.webp" />
    </Frame>
  </Step>

  <Step title="Create and configure a Slack app">
    1. Go to the [Slack Apps page](https://api.slack.com/apps) and click "Create an App".
    2. Choose "From scratch", name your app, and select your development workspace.
    3. Under "OAuth & Permissions", add these Bot Token Scopes: `chat:write`, `im:write`, `users:read`, `users:read.email`.
    4. Click "Install App to Workspace" and authorize.
    5. Copy the **Bot User OAuth Access Token** (starts with `xoxb-`).

    <Frame caption="Slack OAuth Scopes">
      <img src="https://mintcdn.com/courier-4f1f25dc/rYENcCCTyPPrDtw0/assets/slack-oauth-scopes.webp?fit=max&auto=format&n=rYENcCCTyPPrDtw0&q=85&s=99d95719a2dc6d77c14ba8645478df5c" alt="The OAuth and Permissions page in Slack with the bot token scopes added" width="1279" height="920" data-path="assets/slack-oauth-scopes.webp" />
    </Frame>
  </Step>

  <Step title="Design a Slack notification template">
    Go to the Courier <AppLink href="https://app.courier.com/assets/templates">Assets page</AppLink> and click <b>+ New > Message Template</b>.
    Select Slack from your integrations.
    In the sidebar, click the new Slack block to open the Slack template editor.
    Add your message content.
  </Step>

  <Step title="Send a test message">
    Click <b>Preview</b>, then <b>Create Test Event</b>.
    Enter your bot token in the <b>Access Token</b> field.
    Click <b>Send</b>. Your message appears in Slack.
  </Step>
</Steps>

***

## Profile requirements

Slack addresses the recipient by channel, user id, or email, so the profile you send to needs a `slack` object holding an `access_token` and one target. Store both 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="Channel">
    The channel id, not its name. `access_token` is the bot token from your Slack app. It sits on the profile beside the target.

    <CodeGroup>
      ```javascript Node.js highlight={3-6} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          slack: {
            access_token: 'xoxb-xxxxx',
            channel: 'CL2MR6HEX',
          },
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "slack": {
                  "access_token": "xoxb-xxxxx",
                  "channel": "CL2MR6HEX",
              },
          },
      )
      ```

      ```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": {
            "slack": {
              "access_token": "xoxb-xxxxx",
              "channel": "CL2MR6HEX"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          slack: {
            access_token: "xoxb-xxxxx",
            channel: "CL2MR6HEX"
          }
        }
      )
      ```

      ```go Go highlight={6-9} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"slack": map[string]any{
      				"access_token": "xoxb-xxxxx",
      				"channel": "CL2MR6HEX",
      			},
      		},
      	},
      )
      ```

      ```java Java highlight={4-6} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "access_token", "xoxb-xxxxx",
                  "channel", "CL2MR6HEX")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2-5} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'slack' => [
          'access_token' => 'xoxb-xxxxx',
          'channel' => 'CL2MR6HEX',
        ],
      ]);
      ```

      ```csharp C# highlight={7-10} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "slack": {
                  "access_token": "xoxb-xxxxx",
                  "channel": "CL2MR6HEX"
                }
              }
              """
          ),
      };
      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 '{"slack":{"access_token":"xoxb-xxxxx","channel":"CL2MR6HEX"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the Slack channel CL2MR6HEX and my bot token on user_123.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="User id">
    Slack opens a direct message with that user. `access_token` is the bot token from your Slack app. It sits on the profile beside the target.

    <CodeGroup>
      ```javascript Node.js highlight={3-6} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          slack: {
            access_token: 'xoxb-xxxxx',
            user_id: 'UEFNTF6QL',
          },
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "slack": {
                  "access_token": "xoxb-xxxxx",
                  "user_id": "UEFNTF6QL",
              },
          },
      )
      ```

      ```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": {
            "slack": {
              "access_token": "xoxb-xxxxx",
              "user_id": "UEFNTF6QL"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          slack: {
            access_token: "xoxb-xxxxx",
            user_id: "UEFNTF6QL"
          }
        }
      )
      ```

      ```go Go highlight={6-9} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"slack": map[string]any{
      				"access_token": "xoxb-xxxxx",
      				"user_id": "UEFNTF6QL",
      			},
      		},
      	},
      )
      ```

      ```java Java highlight={4-6} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "access_token", "xoxb-xxxxx",
                  "user_id", "UEFNTF6QL")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2-5} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'slack' => [
          'access_token' => 'xoxb-xxxxx',
          'user_id' => 'UEFNTF6QL',
        ],
      ]);
      ```

      ```csharp C# highlight={7-10} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "slack": {
                  "access_token": "xoxb-xxxxx",
                  "user_id": "UEFNTF6QL"
                }
              }
              """
          ),
      };
      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 '{"slack":{"access_token":"xoxb-xxxxx","user_id":"UEFNTF6QL"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the Slack user id UEFNTF6QL and my bot token on user_123.
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Email">
    Slack resolves the address to a user, then direct-messages them. `access_token` is the bot token from your Slack app. It sits on the profile beside the target.

    <CodeGroup>
      ```javascript Node.js highlight={3-6} theme={null}
      const profile = await client.profiles.create('user_123', {
        profile: {
          slack: {
            access_token: 'xoxb-xxxxx',
            email: 'sarah@acme-corp.com',
          },
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      profile = client.profiles.create(
          user_id="user_123",
          profile={
              "slack": {
                  "access_token": "xoxb-xxxxx",
                  "email": "sarah@acme-corp.com",
              },
          },
      )
      ```

      ```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": {
            "slack": {
              "access_token": "xoxb-xxxxx",
              "email": "sarah@acme-corp.com"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      profile = courier.profiles.create(
        "user_123",
        profile: {
          slack: {
            access_token: "xoxb-xxxxx",
            email: "sarah@acme-corp.com"
          }
        }
      )
      ```

      ```go Go highlight={6-9} theme={null}
      profile, err := client.Profiles.New(
      	context.TODO(),
      	"user_123",
      	courier.ProfileNewParams{
      		Profile: map[string]any{
      			"slack": map[string]any{
      				"access_token": "xoxb-xxxxx",
      				"email": "sarah@acme-corp.com",
      			},
      		},
      	},
      )
      ```

      ```java Java highlight={4-6} theme={null}
      ProfileCreateParams params = ProfileCreateParams.builder()
          .userId("user_123")
          .profile(ProfileCreateParams.Profile.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "access_token", "xoxb-xxxxx",
                  "email", "sarah@acme-corp.com")))
              .build())
          .build();
      ProfileCreateResponse profile = client.profiles().create(params);
      ```

      ```php PHP highlight={2-5} theme={null}
      $profile = $client->profiles->create('user_123', profile: [
        'slack' => [
          'access_token' => 'xoxb-xxxxx',
          'email' => 'sarah@acme-corp.com',
        ],
      ]);
      ```

      ```csharp C# highlight={7-10} theme={null}
      ProfileCreateParams parameters = new()
      {
          UserID = "user_123",
          Profile = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
              """
              {
                "slack": {
                  "access_token": "xoxb-xxxxx",
                  "email": "sarah@acme-corp.com"
                }
              }
              """
          ),
      };
      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 '{"slack":{"access_token":"xoxb-xxxxx","email":"sarah@acme-corp.com"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, save the Slack email sarah@acme-corp.com and my bot token on user_123.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

<Tip>
  **Order of precedence.** Set more than one of `channel`, `user_id`, or `email` and Courier uses them in that order.
</Tip>

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

For a one-off with no stored profile, pass it inline instead: `"to": { "slack": { "access_token": "xoxb-xxxxx", "channel": "CL2MR6HEX" } }`.

<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 `slack` 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 `slack`">
    Pass it inline instead and nothing is stored. Swap this `to` object into the call on the other tab.

    <CodeGroup>
      ```javascript Node.js highlight={4-7} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            slack: {
              access_token: "xoxb-xxxxx",
              email: "sarah@acme-corp.com",
            },
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
        },
      });
      ```

      ```python Python highlight={4-7} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "slack": {
                      "access_token": "xoxb-xxxxx",
                      "email": "sarah@acme-corp.com",
                  },
              },
              "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": {
              "slack": {
                "access_token": "xoxb-xxxxx",
                "email": "sarah@acme-corp.com"
              }
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40"
          }
        }'
      ```

      ```ruby Ruby highlight={4-7} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            slack: {
              access_token: "xoxb-xxxxx",
              email: "sarah@acme-corp.com"
            }
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40"
        }
      )
      ```

      ```go Go highlight={4-6} theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfSlackRecipient: &shared.SlackRecipientParam{
      				Slack: shared.SlackParamOfSendToSlackEmail("xoxb-xxxxx", "sarah@acme-corp.com"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      	},
      })
      ```

      ```java Java highlight={4-7} theme={null}
      client.send().message(SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(SlackRecipient.builder()
                  .slack(SendToSlackEmail.builder()
                      .accessToken("xoxb-xxxxx")
                      .email("sarah@acme-corp.com")
                      .build())
                  .build())
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .build())
          .build());
      ```

      ```php PHP highlight={4-7} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'slack' => [
              'access_token' => 'xoxb-xxxxx',
              'email' => 'sarah@acme-corp.com',
            ],
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
        ],
      );
      ```

      ```csharp C# highlight={7-11} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new SlackRecipient
              {
                  Slack = new SendToSlackEmail
                  {
                      AccessToken = "xoxb-xxxxx",
                      Email = "sarah@acme-corp.com",
                  },
              },
              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 '{"slack": {"access_token": "xoxb-xxxxx", "email": "sarah@acme-corp.com"}}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40
      ```

      ```text MCP theme={null}
      With Courier MCP, send my template to sarah@acme-corp.com on Slack.
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Overrides

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

You can override the payload sent to Slack's [chat.postMessage](https://api.slack.com/methods/chat.postMessage) using `providers.slack.override.body`. This is useful for advanced formatting, interactivity, and threading.

### Unfurl links

<CodeGroup>
  ```javascript Node.js highlight={8-14} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      to: {
        user_id: "user_123",
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        slack: {
          override: {
            body: {
              unfurl_links: true,
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={8-14} theme={null}
  response = client.send.message(
      message={
          "to": {
            "user_id": "user_123",
          },
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "providers": {
              "slack": {
                  "override": {
                    "body": {
                      "unfurl_links": True,
                    },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={11-17} 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",
          "providers": {
            "slack": {
              "override": {
                "body": {
                  "unfurl_links": true
                }
              }
            }
          }
        }
      }'
  ```

  ```ruby Ruby highlight={8-14} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        user_id: "user_123"
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        "slack" => {
          override: {
            body: {
              unfurl_links: true
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={10-16} 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"),
  		Providers: shared.MessageProvidersParam{
  			"slack": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"unfurl_links": true,
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={6-7} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().userId("user_123").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .providers(MessageProviders.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "override", java.util.Map.of("body", java.util.Map.of("unfurl_links", true)))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={8-14} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'user_id' => 'user_123',
      ],
      'template' => "nt_01kx4h2jdafq8bk9aftxak4b40",
      'providers' => [
        'slack' => [
          'override' => [
            'body' => [
              'unfurl_links' => true,
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={9-18} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { UserID = "user_123" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "slack",
                  new()
                  {
                      Override = new Dictionary<string, JsonElement>()
                      {
                          { "body", JsonSerializer.SerializeToElement(new { unfurl_links = true }) },
                      },
                  }
              },
          },
      },
  };

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

  ```bash CLI highlight={5} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"user_id": "user_123"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.providers '{"slack": {"override": {"body": {"unfurl_links": true}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to user_123 on Slack and let links unfurl.
  ```
</CodeGroup>

### Slack blocks (Block Kit)

Send rich, interactive layouts using [Slack blocks](https://api.slack.com/block-kit):

<CodeGroup>
  ```javascript Node.js highlight={8-30} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      to: {
        user_id: "user_123",
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        slack: {
          override: {
            body: {
              blocks: [
                {
                  type: "header",
                  text: {
                    type: "plain_text",
                    text: "Welcome!",
                  },
                },
                {
                  type: "section",
                  text: {
                    type: "mrkdwn",
                    text: "This is a *section* block.",
                  },
                },
              ],
              text: "Fallback plain text.",
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={8-30} theme={null}
  response = client.send.message(
      message={
          "to": {
            "user_id": "user_123",
          },
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "providers": {
              "slack": {
                  "override": {
                    "body": {
                      "blocks": [
                        {
                          "type": "header",
                          "text": {
                            "type": "plain_text",
                            "text": "Welcome!",
                          },
                        },
                        {
                          "type": "section",
                          "text": {
                            "type": "mrkdwn",
                            "text": "This is a *section* block.",
                          },
                        },
                      ],
                      "text": "Fallback plain text.",
                    },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={11-33} 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",
          "providers": {
            "slack": {
              "override": {
                "body": {
                  "blocks": [
                    {
                      "type": "header",
                      "text": {
                        "type": "plain_text",
                        "text": "Welcome!"
                      }
                    },
                    {
                      "type": "section",
                      "text": {
                        "type": "mrkdwn",
                        "text": "This is a *section* block."
                      }
                    }
                  ],
                  "text": "Fallback plain text."
                }
              }
            }
          }
        }
      }'
  ```

  ```ruby Ruby highlight={8-30} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        user_id: "user_123"
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        "slack" => {
          override: {
            body: {
              blocks: [
                {
                  type: "header",
                  text: {
                    type: "plain_text",
                    text: "Welcome!"
                  }
                },
                {
                  type: "section",
                  text: {
                    type: "mrkdwn",
                    text: "This is a *section* block."
                  }
                }
              ],
              text: "Fallback plain text."
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={10-32} 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"),
  		Providers: shared.MessageProvidersParam{
  			"slack": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"blocks": []any{
  							map[string]any{
  								"type": "header",
  								"text": map[string]any{
  									"type": "plain_text",
  									"text": "Welcome!",
  								},
  							},
  							map[string]any{
  								"type": "section",
  								"text": map[string]any{
  									"type": "mrkdwn",
  									"text": "This is a *section* block.",
  								},
  							},
  						},
  						"text": "Fallback plain text.",
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={6-7} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().userId("user_123").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .providers(MessageProviders.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "override", java.util.Map.of("body", java.util.Map.of("blocks", java.util.List.of(java.util.Map.of("type", "header", "text", java.util.Map.of("type", "plain_text", "text", "Welcome!")), java.util.Map.of("type", "section", "text", java.util.Map.of("type", "mrkdwn", "text", "This is a *section* block."))), "text", "Fallback plain text.")))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={8-30} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'user_id' => 'user_123',
      ],
      'template' => "nt_01kx4h2jdafq8bk9aftxak4b40",
      'providers' => [
        'slack' => [
          'override' => [
            'body' => [
              'blocks' => [
                [
                  'type' => 'header',
                  'text' => [
                    'type' => 'plain_text',
                    'text' => 'Welcome!',
                  ],
                ],
                [
                  'type' => 'section',
                  'text' => [
                    'type' => 'mrkdwn',
                    'text' => 'This is a *section* block.',
                  ],
                ],
              ],
              'text' => 'Fallback plain text.',
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={9-18} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { UserID = "user_123" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "slack",
                  new()
                  {
                      Override = new Dictionary<string, JsonElement>()
                      {
                          { "body", JsonSerializer.SerializeToElement(new { blocks = new[] { new { type = "header", text = new { type = "plain_text", text = "Welcome!" } }, new { type = "section", text = new { type = "mrkdwn", text = "This is a *section* block." } } }, text = "Fallback plain text." }) },
                      },
                  }
              },
          },
      },
  };

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

  ```bash CLI highlight={5} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"user_id": "user_123"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.providers '{"slack": {"override": {"body": {"blocks": [{"type": "header", "text": {"type": "plain_text", "text": "Welcome!"}}, {"type": "section", "text": {"type": "mrkdwn", "text": "This is a *section* block."}}], "text": "Fallback plain text."}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to user_123 on Slack using Block Kit blocks.
  ```
</CodeGroup>

Design and preview your blocks visually with the [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/).

### Replying in a thread

To reply to a thread, set the `thread_ts` value:

<CodeGroup>
  ```javascript Node.js highlight={8-14} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      to: {
        user_id: "user_123",
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        slack: {
          override: {
            body: {
              thread_ts: "1234567890.123456",
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={8-14} theme={null}
  response = client.send.message(
      message={
          "to": {
            "user_id": "user_123",
          },
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "providers": {
              "slack": {
                  "override": {
                    "body": {
                      "thread_ts": "1234567890.123456",
                    },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={11-17} 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",
          "providers": {
            "slack": {
              "override": {
                "body": {
                  "thread_ts": "1234567890.123456"
                }
              }
            }
          }
        }
      }'
  ```

  ```ruby Ruby highlight={8-14} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        user_id: "user_123"
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      providers: {
        "slack" => {
          override: {
            body: {
              thread_ts: "1234567890.123456"
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={10-16} 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"),
  		Providers: shared.MessageProvidersParam{
  			"slack": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"thread_ts": "1234567890.123456",
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={6-7} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().userId("user_123").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .providers(MessageProviders.builder()
              .putAdditionalProperty("slack", JsonValue.from(java.util.Map.of(
                  "override", java.util.Map.of("body", java.util.Map.of("thread_ts", "1234567890.123456")))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={8-14} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'user_id' => 'user_123',
      ],
      'template' => "nt_01kx4h2jdafq8bk9aftxak4b40",
      'providers' => [
        'slack' => [
          'override' => [
            'body' => [
              'thread_ts' => '1234567890.123456',
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={9-18} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { UserID = "user_123" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "slack",
                  new()
                  {
                      Override = new Dictionary<string, JsonElement>()
                      {
                          { "body", JsonSerializer.SerializeToElement(new { thread_ts = "1234567890.123456" }) },
                      },
                  }
              },
          },
      },
  };

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

  ```bash CLI highlight={5} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"user_id": "user_123"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.providers '{"slack": {"override": {"body": {"thread_ts": "1234567890.123456"}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to user_123 as a reply in an existing Slack thread.
  ```
</CodeGroup>

### Mentioning users

Mention a user in your message using `<@USER_ID>` syntax in your template:

```
Hello <@UEFNTF6QL>, you have a new notification!
```

You can also use variables for dynamic mentions.

### Preventing unwanted mentions

Slack automatically parses text for `@name` patterns and turns them into mentions. If your message content includes text that resembles a mention but shouldn't trigger one, set [`verbatim: true`](https://docs.slack.dev/reference/block-kit/composition-objects/text-object/) on the Block Kit text object to disable this parsing.

Courier's standard template blocks (text, quote, etc.) don't expose the `verbatim` flag. To use it, either pass raw Block Kit JSON via `providers.slack.override.body.blocks` or use a <Doc href="/docs/design/elemental/elements/jsonnet">Jsonnet block</Doc> in Design Studio:

```json theme={null}
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": ">>> Message from @jennifer about the deployment",
    "verbatim": true
  }
}
```

### Slash command responses

If responding to a [Slash Command](https://api.slack.com/interactivity/slash-commands), use the `response_url` as an incoming webhook:

```json theme={null}
{
  "to": {
    "slack": {
      "incoming_webhook": {
        "url": "https://hooks.slack.com/commands/1234/5678"
      }
    }
  }
}
```

Set `override.slack.body.response_type` to `in_channel` or `ephemeral` as needed.

### Incoming webhooks

You can send messages to a channel using a Slack [Incoming Webhook](https://api.slack.com/messaging/webhooks):

```json theme={null}
{
  "to": {
    "slack": {
      "incoming_webhook": {
        "url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
      }
    }
  }
}
```

### Updating notifications

To update a previously sent Slack message, set a "replacement key" (usually `ts`) in your notification template's Slack channel settings. Courier will use this key to update the message instead of posting a new one.

***

## Delivery tracking

When Courier sends a Slack message via Bot OAuth (`chat.postMessage`), it captures Slack's `ts` (message timestamp identifier) and `channel` (conversation ID) from the API response. You can use these values to thread replies, update messages, or link back to the original Slack message.

### Where delivery data appears

Slack-specific fields are returned in the `providers` array of the <Endpoint method="GET" path="/messages/{message_id}" name="Get message" href="/docs/api-reference/messages/get-message" /> response and in <Doc href="/docs/monitor/webhooks/events#payload-shape">`message:updated`</Doc> webhook events:

```json theme={null}
{
  "providers": [
    {
      "provider": "slack",
      "status": "DELIVERED",
      "channel": {
        "key": "direct_message:slack",
        "name": "Slack",
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40"
      },
      "reference": {
        "channel": "C04EXAMPLE",
        "ts": "1630512468.000700"
      },
      "sent": 1630512468691,
      "delivered": 1630512468700
    }
  ]
}
```

| Field                           | Description                                                                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `providers[].reference.ts`      | Slack message timestamp. Use this to [reply in a thread](#replying-in-a-thread) or [update the message](#updating-notifications). |
| `providers[].reference.channel` | Slack conversation ID where the message was posted.                                                                               |
| `providers[].channel`           | Courier routing metadata (template and channel key). Not the Slack channel ID.                                                    |

<Info>
  Slack messages are marked as `DELIVERED` immediately after sending because Slack's API confirms delivery synchronously. The `delivered` and `sent` timestamps will be very close together.
</Info>

<Warning>
  Incoming Webhook sends may not include `reference` data because Slack's Incoming Webhook API does not return `ts` or `channel` in its response.
</Warning>

***

## Troubleshooting

* **Missing or incorrect Slack scopes:**
  * Double-check your app has all required scopes (`chat:write`, `im:write`, `users:read`, `users:read.email`, and `chat:write.public` for channels).
  * Reinstall your Slack app after updating scopes.

* **Bot not invited to channel:**
  * Make sure your Slack app/bot is a member of the channel you want to message.

* **Invalid or missing tokens:**
  * Ensure you are using the correct Bot User OAuth Access Token (starts with `xoxb-`).
  * Never use a user token or an expired token.

* **Permission errors or message not delivered:**
  * Check the <AppLink href="https://app.courier.com/logs/messages">Courier Message Logs</AppLink> for error details and troubleshooting tips.

* **User or channel not found:**
  * Double-check the email, user\_id, or channel ID. For channels, copy the ID from the Slack URL.

* **Message Truncated:**
  * Slack blocks [limit the characters](https://api.slack.com/reference/block-kit/blocks#section) in a single section to 3k characters. Courier automatically truncates Slack messages over 3k characters by removing escape and formating characters that are added by Slack after submitting the block.

If you're still stuck, reach out to Courier support at [support@courier.com](mailto:support@courier.com).

## Provider details

```text theme={null}
slack
```

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>
