Highlight Priority Conversations in Intercom: Python Code Tutorial

Highlight Priority Conversations in Intercom: Python Code Tutorial

Is your CSM team playing a game of ‘guess who’ with your support tickets? You know the drill, hours spent sifting through endless requests, trying to figure out where to focus and what needs immediate attention, only to realize an urgent ticket (from a C-level exec no less) has been missed. Nightmare!

That’s where smart automation comes in. By connecting Surfe’s contact enrichment API with Intercom’s webhooks, you can instantly enrich contact data, identify C-level profiles and tag critical conversations. From there, it’s simple—if they’re a C-level, the conversation is flagged as ‘Priority’ for the CSM team to jump on. No more guess work.

In this guide, we’ll walk through exactly how to set up this smooth integration with FastAPI so you can focus on the tickets that truly matter. Let’s dive in!

 

 

Want the full script? Jump to the bottom for a quick copy-paste or view it on GitHub!

Prerequisites

  1. Python 3.x installed
    Most modern operating systems come with Python 3 pre-installed. To check if Python is installed on your system:
    • Windows: Open Command Prompt (Win + R, type cmd, press Enter) and run:
py --version

 

macOS/Linux: Open Terminal and run:

python3 --version
  • If Python is not installed, download it from the official Python website (or beginner guide to downloads) and follow the installation instructions for your OS.
  • Basic Python knowledge
  • Intercom Account with Developer Hub Access
  • Surfe account and API key

To use Surfe’s API, you’ll need to create an account and obtain an API key. You can find the API documentation and instructions for generating your API key here: Surfe Developer Docs.

Project Setup

  • Creating a Virtual Environment (Optional but Recommended)

To keep your dependencies organized, you can create a virtual environment:

python3 -m venv env # macOS/Linux

source env/bin/activate 

py -m venv env # Windows

env\Scripts\activate  #

 

1.2 Install required dependencies

To install required dependencies we will create a requirements.txt  file in the root of our project directory, with the following packages

fastapi[standard]==0.113.0

pydantic==2.8.0

python-dotenv==1.0.1

requests==2.31.0

uvicorn==0.27.1

 

This is what each of them does:

  • Fast API: Modern Python web framework for building APIs
  • Pydantic: Data validation using Python type hints
  • Python dotenv: Loads environment variables from .env files
  • requests: HTTP library for making API requests
  • Uvicorn:  ASGI server to run FastAPI applications

Then run this command to install them:

pip install -r requirements.txt

1.3 Setting up your environment variables securely

In the root of your project’s directory, create a .env file where we will store the following env variables:

INTERCOM_ACCESS_TOKEN=your_intercom_access_token

SURFE_API_KEY=your_surfe_api_key

WEBHOOK_SECRET=your_webhook_secret

 

  • To obtain your intercom access token navigate to Developer Hub > Select App > Authentication and copy access token
  • To obtain your webhook secret navigate to Developer Hub > Select App > Basic Information and copy client secret

Project Structure

Let’s start by organizing our project with the following file structure. Create these files first, and we’ll go over their purpose and details in the next steps.

intercom-clevel-priority-example/

├── .env                   # Contains environment variables

├── main.py               # Main FastAPI application entry point

├── requirements.txt      # Python dependencies

├── app/                  # Application module directory

│   ├── webhook/         # Webhook module directory

│   │   ├── handler.py   # Webhook handler

│   │   └── __init__.py  # Module initialization

│   ├── services/        # Services module directory

│   │   ├── intercom.py  # Intercom service

│   │   ├── surfe.py     # Surfe service

│   │   └── __init__.py  # Module initialization

│   ├── __init__.py      # Application initialization

│   └── config.py        # Configuration module

Implementation Steps

2.1 Configuration Setup

First, we’ll set up configuration management to securely handle environment variables.

Purpose: Ensures all required credentials for Intercom and Surfe APIs are loaded and validated before the application starts, preventing runtime errors due to missing configurations.

import os

from dotenv import load_dotenv


# Load environment variables

load_dotenv()


# API Configuration

INTERCOM_ACCESS_TOKEN = os.getenv("INTERCOM_ACCESS_TOKEN")

SURFE_API_KEY = os.getenv("SURFE_API_KEY")

WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")


# Validate required environment variables

if not all([INTERCOM_ACCESS_TOKEN, SURFE_API_KEY, WEBHOOK_SECRET]):

   raise ValueError("Missing required environment variables")

2.2 Main Application Entry Point

The main application file initializes our FastAPI instance and defines the core routes.

from typing import Union


from fastapi import FastAPI # type: ignore

from app.webhook.handler import router as webhook_router


app = FastAPI(title="C-Level Priority Handler")


@app.get("/")

async def root():

   return {"message": "Hello World"}


app.include_router(webhook_router, prefix="/webhook", tags=["webhook"])


if __name__ == "__main__":

   import uvicorn # type: ignore

   uvicorn.run(app, host="0.0.0.0", port=8000)

2.3 Service Layer Implementation

In the services module, we will implement utility functions for interacting with both Intercom and Surfe APIs.

2.3.1 Intercom Service

Service Initialization

Here, we configure the base API URL, access token, API version, and the tag ID used to highlight conversations with C-Level customers. You should create a tag via your Intercom dashboard or the Intercom Tag API and save its ID for later use.

class IntercomService:

    def __init__(self):

        self.base_url = "https://api.intercom.io"

        self.headers = {

            "Authorization": f"Bearer {INTERCOM_ACCESS_TOKEN}",

            "Content-Type": "application/json",

            "Intercom-Version": "2.1"

        }

        self.c_level_tag_id = "10853140"

 

Webhook Signature Verification

Next, we will create a function to verify the legitimacy of incoming requests using HMAC-SHA1. Read more on signed notifications in the intercom docs here.

def verify_webhook_signature(self, secret: str, signature: str, message: bytes) -> bool:

    expected_signature = hmac.new(

        key=secret.encode('utf-8'),

        msg=message,

        digestmod=hashlib.sha1

    ).hexdigest()

    

    return hmac.compare_digest(f"sha1={expected_signature}", signature)

 


Contact Details Retrieval

We will also define a function to retrieve a contact’s details using their ID, which will later be used to obtain their email address.

def get_contact_details(self, contact_id: str):

    endpoint = f"{self.base_url}/contacts/{contact_id}"

    response = requests.get(endpoint, headers=self.headers)

    return response.json()

 

Conversation Tagging

The final function will attach a tag to a conversation.

def add_conversation_tag(self, conversation_id: str, admin_id: str):

    endpoint = f"{self.base_url}/conversations/{conversation_id}/tags"

    payload = {

        "id": self.c_level_tag_id,

        "admin_id": admin_id

    }

    response = requests.post(endpoint, json=payload, headers=self.headers)

    # ... error handling ...

 

2.3.2 Surfe Service

Service Initialization

Similar to the Intercom service setup, we define the base API URL and set up the headers with the access token.

class SurfeService:

    def __init__(self):

        self.base_url = "https://api.surfe.com/v1"

        self.headers = {

            "Authorization": f"Bearer {SURFE_API_KEY}",

            "Content-Type": "application/json"

        }

 

Contact Enrichment

Next, we define the only utility function needed for this example: searching for a contact by email and retrieving enriched details, including seniority.

async def get_contact_details_by_email(self, email: str):

    endpoint = f"{self.base_url}/people/search/byEmail"

    payload = {

        "email": email,

        "linkedInURLSufficient": False

    }

    # ... API call and error handling ...

2.4 Final Piece: The Webhook Handler (handler.py):

Purpose: Handles incoming webhooks from Intercom, verifies their authenticity, and coordinates interactions between Intercom and Surfe services.

  1.  HEAD Request Handler

Handles Intercom’s webhook validation request.

@router.head("/intercom")

async def handle_webhook_validation():

    return {}

 

  1. POST Request Setup

Defines the main webhook endpoint with signature header.

@router.post("/intercom")

async def handle_webhook(

    request: Request,

    x_hub_signature: Optional[str] = Header(None)

):

 

  1. Request Verification

We will use the previously defined service method to verify the request’s legitimacy using the x-hub-signature from the request headers.

body = await request.body()

if not x_hub_signature or not intercom.verify_webhook_signature(

    WEBHOOK_SECRET,

    x_hub_signature,

    body

):

    raise HTTPException(

        status_code=401,

        detail="Invalid webhook signature"

    )

 

  1. Topic Validation

The only topic this handler processes  is the conversation.user.created So we ignore any other incoming notification

payload = await request.json()

topic = payload.get("topic")


if topic != "conversation.user.created":

    return {"status": "ignored", "reason": "irrelevant topic"}

 

  1. Data Extraction

Now that we’ve validated the request and confirmed the notification is for a newly created conversation, we extract the necessary data from the payload:

  • conversation_id
  • admin_id
  • contact_id
data = payload.get("data", {})

conversation_id = data.get("item", {}).get("id")

admin_id = data_item.get("source", {}).get("author", {}).get("id")

contact_id = data.get("item", {}).get("contacts", {}).get("contacts", [{}])[0].get("id")

 

  1. Contact Details Retrieval

Next, we use the get_contact_details service method we defined earlier to retrieve the last missing piece of data: the contact’s email.

contact_details_intercom = intercom.get_contact_details(contact_id)

contact_email = contact_details_intercom.get("email", {})

 

  1. Data Validation

We add an extra validation step to ensure that all required data is present before proceeding.

if not all([conversation_id, contact_email, admin_id]):

    raise HTTPException(

        status_code=400,

        detail="Missing required conversation data"

    )

 

  1. Surfe Enrichment

Now, we call the Surfe service’s get_contact_details_by_email method to retrieve the contact’s full details.

contact_details_surfe = await surfe.get_contact_details_by_email(contact_email)

 

  1. C-Level Check and Tag

Determines if the contact is C-level by evaluating the seniority and department fields, then tags the conversation accordingly.

is_c_level = any(seniority.lower() == 'c-level' for seniority in contact_details_surfe.get("person", {}).get("seniorities", [])) or any(department.lower() == 'c suite' for department in contact_details_surfe.get("person", {}).get("departments", []))

if is_c_level:

    await intercom.add_conversation_tag(conversation_id, admin_id)

 

Note: Each section includes error handling and returns appropriate responses. These details are omitted here for brevity but are fully implemented in the full project’s code.

Setting Up Intercom Webhooks

To complete the integration, we need to set up a webhook in the Intercom Developer Hub:

  1. Navigate to your Intercom Developer Hub
  2. Select your app
  3. Go to Webhooks section
  4. Add a new webhook with:
    1. URL: Your application’s webhook endpoint
    2. Topics: Select “conversation.user.created”

Running the Application

  1. Start the application:
 uvicorn main:app --reload

 

  1. For local development, expose your webhook using ngrok:
ngrok http 8000

 

Use the generated ngrok URL as your webhook endpoint in Intercom’s Developer Hub.

Complete Code for Easy Integration

from typing import Union

from fastapi import FastAPI # type: ignore
from app.webhook.handler import router as webhook_router

app = FastAPI(title="C-Level Priority Handler")

@app.get("/")
async def root():
return {"message": "Hello World"}

app.include_router(webhook_router, prefix="/webhook", tags=["webhook"])

if __name__ == "__main__":
import uvicorn # type: ignore
uvicorn.run(app, host="0.0.0.0", port=8000)

Final Notes: Credits, Quotas, and Rate Limiting

Credits & Quotas

Surfe’s API uses a credit system for people enrichment. Retrieving email, landline, and job details consumes email credits, while retrieving mobile phone numbers consumes mobile credits. There are also daily quotas, such as 2,000 people enrichments per day and 200 organization look-alike searches per day. For more information on credits and quotas, please speak to a Surfe representative to discuss a tailored plan that works for you and your business needs. Quotas reset at midnight (local time), and additional credits can be purchased if needed. For full details, refer to the Credits & Quotas documentation.

Rate Limiting

Surfe enforces rate limits to ensure fair API usage. Users can make up to 10 requests per second, with short bursts of up to 20 requests allowed. The limit resets every minute. Exceeding this results in a 429 Too Many Requests error, so it’s recommended to implement retries in case of rate limit issues. Learn more in the Rate Limits documentation.

Surfe is trusted by 30000 sales people wordwide

Ready to supercharge your workflow?

Try Surfe today and ensure your team never misses a critical conversation again.

Contact Enrichment API FAQs

What Is A Contact Enrichment API?
A contact enrichment API is a tool that enhances your contact records with missing or additional information, such as seniority level, job title, and department. For CSMs, this means instantly enriching contact details from support tickets, enabling you to quickly identify high-priority conversations—such as those from C-level executives—without having to manually search or cross-reference data. With a contact enrichment API, you’ll ensure that your team is focused on the conversations that matter most, without wasting time on low-priority tickets.

Why Do CSM Teams Need A Contact Enrichment API?
CSMs need to prioritize their time on the conversations that drive impact. By using a contact enrichment API, you can quickly and easily identify when a support ticket is from a decision-maker or other high-priority contact. This means your team can focus on what’s truly important—following up on urgent issues or potential opportunities—without sifting through endless support tickets to figure out who’s who. With contact enrichment, you can streamline your workflow and ensure nothing slips through the cracks.

What’s The Difference Between A Contact Enrichment API And Other Data Tools?
Unlike traditional data tools that require manual research or data entry, a contact enrichment API integrates directly into your existing systems (like Intercom) to enhance support ticket data in real time. There’s no need for CSM teams to switch between multiple platforms or search through databases—everything is enriched and ready to go as soon as the ticket arrives. This seamless integration saves time, reduces errors, and ensures that your team always has the most accurate and up-to-date contact data.

Why Is Surfe’s Contact Enrichment API The Best Choice?
Surfe’s contact enrichment API offers fast, accurate enrichment with a 93% find rate, making it a reliable choice for CSM teams who need to quickly flag priority conversations. Unlike other tools that may slow down under heavy use, Surfe delivers real-time enrichment without delays, ensuring that every contact is instantly verified. Whether you’re processing a single support ticket or managing high volumes, Surfe’s API scales to meet your needs. By automating contact enrichment, you’ll be able to focus on engaging with C-level executives and other key decision-makers, ensuring your team’s efforts are spent where they have the most impact.