---
title: "Slack Approval Notification"
slug: "slack-approval-notification"
updated: 2025-11-07T16:35:48Z
published: 2025-11-07T16:35:48Z
canonical: "knowledge-base.rossum.ai/slack-approval-notification"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://knowledge-base.rossum.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack Approval Notification

### Introduction

This extension allows you to send a notification to a Slack DM when a invoice entered the workflow for approval. The notification in Slack contains a button to go directly to the approval task in Rossum.

![](https://cdn.document360.io/1bb6f6bc-c04c-4ace-a1e8-8c4cfd3fbc98/Images/Documentation/Slack%20Approval%20Notification%2001v2.png)

### Prerequisites

1. Access to the Slack Workspace
2. Create an Slack App on [https://api.slack.com/apps/](https://api.slack.com/apps/) (ask your workspace administrator for access).
3. Get the Bot User OAuth Token for API access to your workspace

### Extension settings

1. Create an extension
2. Triggered event: `Document status: changed`
3. Queues: select your `queues`
4. Additional metadata: `none`
5. Runtime: `python3.12`
6. Select `token owner`

### Configuration JSON

Configuration contains the email address of the slack user to sent the DM to. This is for demo purposes only, so you receive the notifications.

settings:

```json
{
  "slack_user_email": "example@rossum.ai"
}
```

secrets:

```json
{
  "slack_token": "copy_slack_token_here"
}
```

### Python code

Copy following code in

```python
import requests
import json
from typing import List, Optional

def rossum_hook_request_handler(payload: dict) -> dict:
    
    messages = []
    operations = []

    #general settings   
    slackuser = payload["settings"]["slack_user_email"]
    slacktoken = payload["secrets"]["slack_token"]
    base_url = payload["base_url"]
    rossum_token = payload["rossum_authorization_token"]
    
    #annotation data    
    status = payload["annotation"]["status"]
    annotation_id = payload["annotation"]["id"]
    annotation_content_url = payload["annotation"]["content"]

    # get annoation data
    annotation_content = get_annotation_content(annotation_content_url, rossum_token)
  
    # check if status is to_review, only then sent slack notification
    if status == "in_workflow":
        user = find_slackuser_by_email(slackuser, slacktoken)
        send_slack_message(user, slacktoken, annotation_id, base_url, annotation_content["content"])
        
    
    messages = [create_message("info", "slack notification sent")]
    return {"messages": messages, "operations": operations}

def find_slackuser_by_email(slackuser, slacktoken):
    
    url = "https://slack.com/api/users.lookupByEmail?email=" + slackuser
    header = {"Authorization": "Bearer " + slacktoken}
    response = requests.get(url, headers=header)

    return response.json()["user"]
    
    

def send_slack_message(user, slacktoken, annotation_id, base_url, annotation_content):
    
    userid = user["id"]
    realname = user["real_name"]
    
    # Extract invoice information
    invoicenumber = find_by_schema_id(annotation_content, "document_id")["content"]["value"]
    vendorname = find_by_schema_id(annotation_content, "sender_name")["content"]["value"]
    duedate = find_by_schema_id(annotation_content, "date_due")["content"]["value"]
    amount_total = find_by_schema_id(annotation_content, "amount_total")["content"]["value"]
    currency = find_by_schema_id(annotation_content, "currency")["content"]["value"].upper()

    # Get document URL for direct access
    document_url = base_url + "/requests/" + str(annotation_id)
  
    # Prepare Slack message
    blocks =  [
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": "📋 Invoice Approval Required"
                }
            },
            {
                "type": "section",
                "fields": [
                    {
                        "type": "mrkdwn",
                        "text": f"*Assigned to:*\n{realname}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Status:*\nPending Approval"
                    }
                ]
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "*Invoice Details:*"
                }
            },
            {
                "type": "section",
                "fields": [
                    {
                        "type": "mrkdwn",
                        "text": f"*Vendor:*\n{vendorname}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": f"*Invoice Number:*\n{invoicenumber}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": f"*Amount:*\n{amount_total} {currency}"
                    },
                    {
                        "type": "mrkdwn",
                        "text": f"*Due Date:*\n{duedate}"
                    }
                ]
            },
            {
                "type": "actions",
                "elements": [
                    {
                        "type": "button",
                        "text": {
                            "type": "plain_text",
                            "text": "Review Invoice"
                        },
                        "style": "primary",
                        "url": document_url
                    }
                ]
            }
        ]
    

    url = "https://slack.com/api/chat.postMessage"
    header = {"Authorization": "Bearer " + slacktoken}
    formdata = {"channel": userid, "text":"An invoice requires your approval", "blocks": json.dumps(blocks)}
    
    response = requests.post(url, headers=header, data=formdata)
    return

    
def create_message(message_type, message_content, datapoint_id=None):
    return {
        "content": message_content,
        "type": message_type,
        "id": datapoint_id,
    }

def get_annotation_content(annotationurl: str, rossum_token: str) -> dict:
    url = annotationurl
    header = {"Authorization": "Bearer " + rossum_token}
    response = requests.get(url, headers=header)

    return response.json()
    
    

def find_by_schema_id(content, schema_id: str) -> Optional[dict]:
    """
    Return the first datapoint matching a schema id or None if no such element exists.
    :param content: annotation content tree (see https://api.elis.rossum.ai/docs/#annotation-data)
    :param schema_id: field's ID as defined in the extraction schema(see https://api.elis.rossum.ai/docs/#document-schema)
    :return: the datapoint matching the schema ID
    """
    for node in content:
        if node["schema_id"] == schema_id:
            return node
        elif "children" in node:
            # if the element with specified schema_id was found
            if found := find_by_schema_id(node["children"], schema_id):
                return found
    return None
```
