Slack Commands for Efficient Tax Document Distribution

Slackbot using Lambda functions

There are three ways of doing things, easy, complex and the e-verse way (AKA the fun way).

Once a month we need our developers to receive a document with their updated information for taxing reasons. It could be easy to deploy a simple endpoint to login, check our database and return a file. But what if we use our main communication platform to do it?

What is Slack?

Slack is a cloud-based collaboration tool launched in 2013, designed to streamline workplace communication. It offers organized channels for team discussions, direct messaging for private conversations, and integrates seamlessly with numerous business applications. With its user-friendly interface and powerful search capabilities, Slack reduces email clutter, enhances productivity, and fosters a connected work environment.

Why Slack commands?

Slack is currently the ubiquitous communication platform used in all industries. 

A slack command is an app triggered by a “/” followed by a keyword, which triggers a POST request to an API in the background. 

Here is a sample of the information sent by the command

&api_app_id=A123456console.log( 'Code is Poetry' );

What’s the catch?

Slack commands have a timeout of 3 seconds which is not enough to query the database, create and post the pdf. Have in mind Lambda functions need a few milliseconds to be up and running. All this makes it impossible to process everything on a single API call.

The good news is that we don’t need to response to the slack command with the PDF itself! We could send it as a file to a channel shared between the user and the App.

At first we tried to send a quick response to the command and leave the pdf creation process on a separated thread on the same lambda function, but the process never ended correctly. We suspect Lambda functions may have a time limit after sending a response, which can stop other background threads from running.

To solve this problem, we utilize a second Lambda function. This function is responsible for receiving the request to create and post the PDF to the App channel. It does not require sending back a response.

Proposed architecture

API Gateway

An API Gateway is a server-side architectural component that acts as an intermediary between external consumers and a collection of microservices. It centralizes external access, ensuring that requests are processed efficiently and securely. The gateway can handle tasks like request routing, composition, rate limiting, and authentication, thereby simplifying the underlying microservices and promoting scalability. By abstracting the underlying system’s complexity, an API Gateway provides a streamlined interface for client applications, ensuring consistent and optimized interactions with backend services.

The API gateway exposes a POST method pointing to the main Lambda function.

What is AWS Lambda?

AWS Lambda is a serverless computing service provided by Amazon Web Services (AWS). It allows developers to run code in response to specific events without provisioning or managing servers. Users simply upload their code, and Lambda automatically and precisely scales execution in real-time, charging only for the compute time consumed. This service integrates seamlessly with other AWS services, making it a versatile tool for diverse applications, from data processing to real-time file and stream processing

Slack Command Lambda

The Slack Command Lambda receives the request creates a call to the SlackPoster function and returns a response message right away.

The key here is to set the Lambda invocation as an Event. We saw that not only Lambda is called correctly but if an error happens it runs one more time until a result is returned.

					public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request,
 ILambdaContext context)
    APIGatewayProxyResponse _APIGatewayProxyResponse = new APIGatewayProxyResponse();
    _APIGatewayProxyResponse.StatusCode = 200;
    _APIGatewayProxyResponse.Headers = new Dictionary<string, string>();
    _APIGatewayProxyResponse.Headers.Add("Content-Type", "application/json");
    _APIGatewayProxyResponse.Body = "In progress";

    var Parameters = HttpUtility.ParseQueryString(request.Body);
    using (AmazonLambdaClient client = new AmazonLambdaClient(RegionEndpoint.USEast1))
        await client.InvokeAsync(new Amazon.Lambda.Model.InvokeRequest()
            FunctionName = "slackbotPoster",
            InvocationType = InvocationType.Event,
            Payload = JsonSerializer.Serialize(Parameters.AllKeys.ToDictionary(k => k, k => Parameters[k]))

    return _APIGatewayProxyResponse;

Here is a snip of the code we used. Note we return an APIGatewayProxyResponse

⚠️ Note: you need to give permissions for Lambda functions to AssumeRole to call the second Lambda

SlackPoster Lambda

Once we have the information we need to create a PDF. Without the procession of returning a quick response, we can take our time to create the payload (less than 5 minutes, that’s the limit of Lambda functions.

					public class Function
    /// <summary>
    /// Creates a PDF based on parameters
    /// </summary>
    /// <param name="Parameters"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public string FunctionHandler(Dictionary<string?, string?> Parameters, ILambdaContext context)
            Tax _tax= new Tax(Parameters);
        catch (Exception ex)
            LambdaLogger.Log("Error: " + ex.Message);

        return "OK";

The magic happens inside the method in charge of uploading the PDF. Using the parameters from the Slack command we know the user name and the channel shared with the bot.

We post the PDF as a byte array directly to the user’s bot channel

					private async void UploadPDF(byte[] pdfString, string channel_id, string user_name)
    using (var httpClient = new HttpClient())
        HttpResponseMessage? response = null;
        var request = new HttpRequestMessage(new HttpMethod("POST"), "https://slack.com/api/files.upload");
        JsonElement JO = new JsonElement();

        request.Headers.TryAddWithoutValidation("Authorization", "Bearer XXXX");

        var multipartContent = new MultipartFormDataContent();
        multipartContent.Add(new ByteArrayContent(pdfString), "file", "XXXX.pdf");
        multipartContent.Add(new StringContent($"Hello, {user_name}. Here is your new tax information"), "initial_comment");
        multipartContent.Add(new StringContent(channel_id, System.Text.Encoding.UTF8), "channels");
        request.Content = multipartContent;

            var e = await httpClient.SendAsync(request);
            var content = e.Content.ReadAsStringAsync().Result;
            LambdaLogger.Log("PDF upload process result: " + content);
        catch (Exception ex)
            LambdaLogger.Log("PDF upload process failed, retrying: " + ex.Message);
            UploadPDF(pdfString, channel_id, user_name);


We discover that creating a slackbot is a simple task. They not only enhanced our current communication workflows but also can help on other areas such as accounting, management and production.

It only takes a little bit of effort to learn and deploy.

What do you think? Share with us your ideas of good implementations for a Slackbot.



I'm an Architect who decided to make his life easier by coding. Curious by nature, I approach challenges armed with lateral thinking and a few humble programming skills. Love to work with passioned people and push the boundaries of the industry. Bring me your problems/Impossible is possible, but it takes more time.

Helpful Links: