Sending Messages
slackblocks produces the JSON payloads that Slack APIs accept; it does not make HTTP calls itself. Send those payloads with the established Slack client for your language or any HTTP client.
The trick is the ** operator: a slackblocks.Message is a mapping, so you can unpack it directly into client.chat_postMessage(...).
With the modern slack-sdk
- Python (slackblocks)
- JSON Message
- Equivalent `curl`
- Slack UI
from os import environ
from slack_sdk import WebClient
from slackblocks import Message, SectionBlock
client = WebClient(token=environ["SLACK_API_TOKEN"])
block = SectionBlock("Hello, world!")
message = Message(channel="#general", blocks=block)
response = client.chat_postMessage(**message)
{
"channel": "#general",
"mrkdwn": true,
"blocks": [
{
"type": "section",
"block_id": "992ceb6b-9ad4-496b-b8e6-1bd8a632e8b3",
"text": {
"type": "mrkdwn",
"text": "Hello, world!"
}
}
]
}
Note: the block_id field is a pseudorandomly generated UUID. Pass an explicit block_id to any block constructor if you need deterministic IDs (e.g. for testing or interaction handling).
curl -H "Content-type: application/json" \
--data '{"channel":"#general","blocks":[{"type":"section","block_id":"992ceb6b-9ad4-496b-b8e6-1bd8a632e8b3","text":{"type":"mrkdwn","text":"Hello, world!"}}]}' \
-H "Authorization: Bearer ${SLACK_API_TOKEN}" \
-X POST https://slack.com/api/chat.postMessage

With the legacy slackclient
The API is identical — only the import path changes:
from os import environ
from slack import WebClient # legacy slackclient package
from slackblocks import Message, SectionBlock
client = WebClient(token=environ["SLACK_API_TOKEN"])
message = Message(channel="#general", blocks=SectionBlock("Hello, world!"))
response = client.chat_postMessage(**message)
Other delivery surfaces
slackblocks provides specialized message classes for each Slack delivery surface. They all unpack the same way as Message.
Incoming webhooks
from slack_sdk.webhook import WebhookClient
from slackblocks import WebhookMessage, SectionBlock
webhook = WebhookClient(url="https://hooks.slack.com/services/...")
message = WebhookMessage(blocks=SectionBlock("Build complete :white_check_mark:"))
webhook.send(**message)
WebhookMessage supports webhook-only options like response_type, replace_original, and delete_original.
Slash command / interaction responses
When responding to a slash command or interactive payload, use MessageResponse:
from slackblocks import MessageResponse, SectionBlock
response_body = MessageResponse(
blocks=SectionBlock("Got it! Working on that now..."),
ephemeral=True, # only visible to the invoking user
).json()
Modals & home tabs
For modals and home tab views, build a Modal (or HomeTabView) and pass it to views_open / views_publish:
from slack_sdk import WebClient
from slackblocks import Modal, SectionBlock
client = WebClient(token=environ["SLACK_API_TOKEN"])
modal = Modal(
title="Confirm action",
blocks=[SectionBlock("Are you sure?")],
submit="Yes",
close="Cancel",
)
client.views_open(trigger_id=trigger_id, view=modal.to_dict())
See Modals reference and Views reference for the full surface.
Sending without an SDK
Because Message renders to a plain dict, you can also send it directly:
import json
import os
import urllib.request
from slackblocks import Message, SectionBlock
message = Message(channel="#general", blocks=SectionBlock("Hello, world!"))
req = urllib.request.Request(
"https://slack.com/api/chat.postMessage",
data=message.json().encode("utf-8"),
headers={
"Content-Type": "application/json; charset=utf-8",
"Authorization": f"Bearer {os.environ['SLACK_API_TOKEN']}",
},
)
urllib.request.urlopen(req)
With @slack/web-api
.build() returns a plain Slack-shaped object, so pass the payload directly to the official client:
import { WebClient } from "@slack/web-api";
import { Message, SectionBlock } from "@nicklambourne/slackblocks";
const client = new WebClient(process.env.SLACK_API_TOKEN);
const payload = Message()
.channel("C0123456")
.blocks(SectionBlock().text("Hello, world!"))
.build();
await client.chat.postMessage(payload);
Use a channel ID rather than a display name when possible. Provide a text fallback for notifications and accessibility in production messages.
Incoming webhooks
import { IncomingWebhook } from "@slack/webhook";
import { SectionBlock, WebhookMessage } from "@nicklambourne/slackblocks";
const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL!);
await webhook.send(
WebhookMessage()
.blocks(SectionBlock().text("Build complete :white_check_mark:"))
.build(),
);
Slash-command and interaction responses
Construct the response body, then return it through your web framework:
import { MessageResponse, SectionBlock } from "@nicklambourne/slackblocks";
const responseBody = MessageResponse()
.blocks(SectionBlock().text("Got it! Working on that now..."))
.responseType("ephemeral")
.build();
Modals and home tabs
import { Modal, SectionBlock } from "@nicklambourne/slackblocks";
const view = Modal()
.title("Confirm action")
.blocks(SectionBlock().text("Are you sure?"))
.submit("Yes")
.close("Cancel")
.build();
await client.views.open({ trigger_id: triggerId, view });
Sending without an SDK
await fetch("https://slack.com/api/chat.postMessage", {
method: "POST",
headers: {
authorization: `Bearer ${process.env.SLACK_API_TOKEN}`,
"content-type": "application/json",
},
body: JSON.stringify(payload),
});
With slack-go/slack
slackblocks block builders implement slack.Block, so they fit directly into the native options accepted by github.com/slack-go/slack. Keep the channel, fallback text, threading, unfurling, and other delivery options in slack-go; use slackblocks for the Block Kit content.
package example
import (
"context"
"os"
slackblocks "github.com/nicklambourne/slackblocks/go/v2"
slack "github.com/slack-go/slack"
)
func send(ctx context.Context) (string, error) {
client := slack.New(os.Getenv("SLACK_API_TOKEN"))
_, timestamp, err := client.PostMessageContext(
ctx,
"C0123456",
slack.MsgOptionText("Hello from slackblocks!", false),
slack.MsgOptionBlocks(
slackblocks.NewSectionBlock().Text("Hello, world!"),
),
)
return timestamp, err
}
slack-go marshals each builder while applying MsgOptionBlocks. That runs slackblocks validation before any HTTP request is made and preserves newer block types and fields even when the pinned slack-go release does not model them itself.
Higher-level components expand to the same native type:
blocks, err := slackblocks.NewPaginator().
Blocks(
slackblocks.NewSectionBlock().Text("Result one"),
slackblocks.NewSectionBlock().Text("Result two"),
).
ActionIDPrefix("results").
PageSize(1).
SlackBlocks()
if err != nil {
return err
}
_, _, err = client.PostMessageContext(
ctx,
"C0123456",
slack.MsgOptionBlocks(blocks...),
)
Incoming webhooks
Use slack-go's webhook envelope and put slackblocks builders in its Blocks collection:
message := &slack.WebhookMessage{
Text: "Build complete",
Blocks: &slack.Blocks{BlockSet: []slack.Block{
slackblocks.NewSectionBlock().Text("Build complete :white_check_mark:"),
}},
}
return slack.PostWebhookContext(ctx, os.Getenv("SLACK_WEBHOOK_URL"), message)
Slash-command and interaction responses
Use NewMessageResponse() for the immediate JSON body returned by a slash-command or interaction endpoint. Build() returns an Object, so encode it with encoding/json and return it through your web framework.
Modals and App Home
Use slack-go's native view request types for the view envelope and slackblocks builders for its blocks:
modal := slack.ModalViewRequest{
Type: slack.VTModal,
Title: slack.NewTextBlockObject("plain_text", "Confirm action", false, false),
Submit: slack.NewTextBlockObject("plain_text", "Yes", false, false),
Close: slack.NewTextBlockObject("plain_text", "Cancel", false, false),
Blocks: slack.Blocks{BlockSet: []slack.Block{
slackblocks.NewSectionBlock().Text("Are you sure?"),
}},
}
_, err = client.OpenViewContext(ctx, triggerID, modal)
For App Home, place the same builders in slack.HomeTabViewRequest:
home := slack.HomeTabViewRequest{
Type: slack.VTHomeTab,
Blocks: slack.Blocks{BlockSet: []slack.Block{
slackblocks.NewSectionBlock().Text("Welcome home!"),
}},
}
_, err = client.PublishViewContext(ctx, slack.PublishViewContextRequest{
UserID: userID,
View: home,
})
Sending without a client library
If an application deliberately does not use slack-go, NewMessage().Build() still returns an ordinary JSON-compatible payload. Set its channel and delivery fields there, marshal it with encoding/json, and post it with net/http or another client.