Sending data to Slack channel from your app

This is the part two of our ongoing series on creating a complete Slack app and connecting with our rails app.

  • Module#1 Introduction
  • Module#2 Slack Web Hooks (sending data from your project to Slack)
  • Module#3 API creation, Slash commands (sending data from Slack to your project)
  • Module#4 Slack custom forms (sending data from Slack to your project interactive way)
  • Module#5 Creating Slack Bot (posting message to Slack as a bot user)
  • Module#6 Handling installation of your custom app to user's Slack Channel
  • Module#7 Releasing yout app in Slack Store

Creating a slack app

Go to api.slack.com and create a slack app for yourself.

After you're done, you'll be greeted with the control panel of the app.

Now we need to enable the WebHook feature for the app.

when you try to enable it, you'll be asked to select which workspace your app is connecting with.

Now, we are done configuring from Slack side. Simple as that.

You'll be given the post url where you'll send your api call. Just send a post message on the url on this structure {text: "your message"} and you'll get the message posted on Slack.

Creating the todo app

Just a basic todo app, which sits behind devise authentication. Enabling devise authentication should be easy enough for you guys to google, so I'm skipping that part. Now, lets create a Task model using the scaffold command.

rails g scaffold Task title start:datetime end:datetime done:boolean

We will modify the TasksControllerto call on the webhook whenever we do any action.

So, lets create a class ExternalApiCaller to post on the SlackWebhook for us.

require "net/http"
require "uri"
require "json"

class ExternalApiCaller
  def call uri, data
    uri = URI.parse uri

    request = Net::HTTP::Post.new(uri)

    req_options = { use_ssl: uri.scheme == "https" }
    request.content_type = "application/json"
    request.body = JSON.dump(data)

    response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|

    return response.body

Here's a demo of it in action.

Now we modify our controller to fire this method whenever CRUD actions were done.

require "./app/services/external_api_caller"

class TasksController < ApplicationController
  before_action :authenticate_user!
  before_action :set_task, only: [:show, :edit, :update, :destroy]

  URI = "https://hooks.slack.com/services/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"

  # GET /tasks
  # GET /tasks.json
  def index
    @tasks = Task.all

  # GET /tasks/1
  # GET /tasks/1.json
  def show

  # GET /tasks/new
  def new
    @task = Task.new

  # GET /tasks/1/edit
  def edit

  # POST /tasks
  # POST /tasks.json
  def create
    @task = Task.new(task_params)

    respond_to do |format|
      if @task.save
        format.html { redirect_to @task, notice: 'Task was successfully created.' }
        format.json { render :show, status: :created, location: @task }

          ["#{current_user} created a new task: `#{@task.title}`",
           "\n", "#{@task.get_url}"].join)
        format.html { render :new }
        format.json { render json: @task.errors, status: :unprocessable_entity }

  # PATCH/PUT /tasks/1
  # PATCH/PUT /tasks/1.json
  def update
    respond_to do |format|
      if @task.update(task_params)
        format.html { redirect_to @task, notice: 'Task was successfully updated.' }
        format.json { render :show, status: :ok, location: @task }

          ["#{current_user} has updated task: `#{@task.title}`",
           "\n", "#{@task.get_url}"].join)
        format.html { render :edit }
        format.json { render json: @task.errors, status: :unprocessable_entity }

  # DELETE /tasks/1
  # DELETE /tasks/1.json
  def destroy
    title = @task.title
    respond_to do |format|
      format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
      format.json { head :no_content }

    send_slack_notification("#{current_user} has removed the task:`#{title}`")

    # Use callbacks to share common setup or constraints between actions.
    def set_task
      @task = Task.find(params[:id])

    def send_slack_notification message
      return unless message.present?
      eac = ExternalApiCaller.new
      eac.call URI, {text: message}

    # Never trust parameters from the scary internet, only allow the white list through.
    def task_params
      params.require(:task).permit(:title, :description, :start, :end, :done)

And we're done !

Here's the workspace for the app c9.io/salekin.slack_webhook.

and here's the git repo.

Finishing thoughts

We could improve upon the app with adding Activity gem and call our ExternalApiCaller on each activity

Reference Material

Slack Documentation

All Rights Reserved

Let's register a Viblo Account to get more interesting posts.