Cisco Secure Access Destination Lists overview

Destination Lists API Guide

This guide provides Python client samples for the Cisco Secure Access Destination Lists API.

Note: Your Secure Access API key must have the permissions to read and write on the policies key scope. For more information about the API key scopes, see Secure Access OAuth 2.0 Scopes.

First get your Secure Access API key, set up your environment, and install the Secure Access API client. For more information, see Samples Overview.

Run the Script

  1. Copy the script to a local file called main.py. Locate the script in your environment in a directory above the cisco directory.
  2. Run python3 main.py.

main.py

"""
Copyright (c) 2025 Cisco and/or its affiliates.
This software is licensed to you under the terms of the Cisco Sample
Code License, Version 1.1 (the "License"). You may obtain a copy of the
License at

https://developer.cisco.com/docs/licenses

All use of the material herein must be in accordance with the terms of
the License. All rights not expressly granted by the License are
reserved. Unless required by applicable law or agreed to separately in
writing, software distributed under the License is distributed on an "AS
IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
"""

import requests
import pandas as pd
import json
import os
from dotenv import load_dotenv

from cisco.secure_access import write_to_csv
from cisco.secure_access import API
from cisco.secure_access import policies
from cisco.secure_access import GET
from cisco.secure_access import POST
from cisco.secure_access import PATCH
from cisco.secure_access import DELETE
from cisco.secure_access import token_url
from cisco.secure_access import client_id
from cisco.secure_access import client_secret

# Destination Lists API endpoints
destination_lists_endpoint = "destinationlists"
destination_lists_details_endpoint = "destinationlists/{}"
destinations_details_endpoint = "destinationlists/{}/destinations"
destinations_remove_endpoint = "destinationlists/{}/destinations/remove"

load_dotenv()

# The directory where to write out files
output_dir = os.environ.get('OUTPUT_DIR') or os.get_env['OUTPUT_DIR']

# sample csv files
destination_lists_csv_file = output_dir + "/destination_lists.csv"
destinations_csv_file = output_dir + "/destinations.csv"

def get_all_destination_lists(api, csv_file=None):
    ''' Get Destination Lists. '''
    try:
        # Initialize API pagination variables
        hasMore = True
        page = 1

        while hasMore:
            url = destination_lists_endpoint + "?page=" + str(page) + "&limit=100"
            # Get Destination Lists in the organization
            response = api.Query(policies, url, GET)

            # Check if the API request was successful
            if response.status_code == 200:
                print(f"Success. GET {url}.")
                if len(response.json()['data']) > 0:
                    df = pd.json_normalize(response.json()['data'])
                    write_to_csv(df, csv_file)
                    page += 1
                else:
                    hasMore = False
            else:
                print(f"Failed to get the Destination Lists. Status code: {response.status_code}, Response: {response.text}.")
                hasMore = False
        return df['id'].to_list() if page > 1 else None
    except requests.exceptions.RequestException as e:
        print(f"An error occurred: {e}.")

def get_destinations(api, destination_list_id=None, csv_file=None):
    ''' Get the destinations in the Destination List. '''
    try:
        if destination_list_id is None or csv_file is None:
            raise ValueError("destination_list_id and csv_file are required to get the destinations in the Destination List.")

        # Initialize API pagination variables
        hasMore = True
        page = 1

        while hasMore:
            url = destinations_details_endpoint.format(destination_list_id) + "?page=" + str(page) + "&limit=100"
            # Get Destinations in Destination Lists
            response = api.Query(policies, url, GET)

            # Check if the API request was successful
            if response.status_code == 200:
                print(f"Success. GET {url}.")
                if len(response.json()['data']) > 0:
                    df = pd.json_normalize(response.json()['data'])
                    write_to_csv(df, csv_file)
                    page += 1
                else:
                    hasMore = False
            else:
                print(f"Failed to get the destinations in the Destination List {destination_list_id}. Status code: {response.status_code}, Response: {response.text}.")
                hasMore = False
        return df['id'].to_list() if page > 1 else None
    except Exception as e:
        print(f"An error occurred: {e}.")

def get_destination_lists(api, destination_list_id=None):
    ''' Get the Destination List '''
    try:
        if destination_list_id is None:
            raise ValueError("destination_list_id is required to get the destinations in the Destination List.")
        url = destination_lists_details_endpoint.format(destination_list_id)

        # Get the Destination List
        response = api.Query(policies, url, GET)

        # Check the response status
        if response.status_code == 200:
            print(f"Success. GET {url}.")
            return response.json()
        else:
            print(f"Failed to get the Destination List. Status code: {response.status_code}, Response: {response.text}.")
            return None
    except Exception as e:
        print(f"An error occurred: {e}.")

def patch_destination_lists(api, destination_list_id=None, payload=None):
    ''' Update the properties of the Destination List. '''
    try:
        if destination_list_id is None or payload is None:
            raise ValueError("destination_list_id and payload are required to update the Destination List.")

        url = destination_lists_details_endpoint.format(destination_list_id)

        # Update the name of the Destination List
        response = api.Query(policies, url, PATCH, payload)

        # Check if the API request was successful
        if response.status_code == 200:
            print(f"Success. PATCH {url}, {response.json()}.")
            return response.json()
        else:
            print(f"Failed to update the Destination List {destination_list_id} with {payload['name']}. Status code: {response.status_code}, Response: {response.text}.")
            return
    except Exception as e:
        print(f"An error occurred: {e}.")

def post_destination_lists(api, payload=None):
    ''' Create a Destination List. '''
    try:
        if payload is None:
            raise ValueError("payload is required to create the Destination List.")

        # Create Rule
        response = api.Query(policies, destination_lists_endpoint, POST, payload)

        # Check the response status
        if response.status_code == 200:
            print(f"Success. POST {destination_lists_endpoint}, {response.json()}")
            return response.json()
        else:
            print(f"Failed to create the Destination List. Status code: {response.status_code}, Response: {response.text}.")
            return None
    except Exception as e:
        print(f"An error occurred: {e}.")

def post_destinations(api, destination_list_id = None, payload=None):
    ''' Add destinations to a Destination List. '''
    try:
        if destination_list_id is None or payload is None:
            raise ValueError("destination_list_id and payload are required to create the Destination List.")

        url = destinations_details_endpoint.format(destination_list_id)

        # Create Rule
        response = api.Query(policies, url, POST, payload)

        # Check the response status
        if response.status_code == 200:
            print(f"Success. POST {url}, {response.json()}")
            return response.json()
        else:
            print(f"Failed to add the destinations to the Destination List {destination_list_id}. Status code: {response.status_code}, Response: {response.text}.")
            return None
    except Exception as e:
        print(f"An error occurred: {e}.")

def delete_destinations(api, destination_list_id=None, payload=None):
    ''' Delete destinations in the Destination List. '''
    try:
        if destination_list_id is None or payload is None:
            raise ValueError("destination_list_id and payload are required to delete the destinations in the Destination List.")
        url = destinations_remove_endpoint.format(destination_list_id)

        # Delete the destinations in the Destination List
        response = api.Query(policies, url, DELETE, payload)

        # Check if the API request was successful
        if response.status_code == 200:
            print(f"DELETE {url} Response: {response.text}.")
            return response.json()
        else:
            print(f"Failed to delete the destinations in Destination List {destination_list_id}. Status code: {response.status_code}, Response: {response.text}.")
    except Exception as e:
        print(f"An error occurred: {e}.")

def delete_destination_lists(api, destination_list_id):
    ''' Delete the Destination List. '''
    try:
        if destination_list_id is None:
            raise ValueError("destination_list_id required to delete the Destination List.")
        url = destination_lists_details_endpoint.format(destination_list_id)

        # Delete the Destination List
        response = api.Query(policies, url, DELETE)

        # Check if the API request was successful
        if response.status_code == 200:
            print(f"DELETE {url} Response: {response.text}.")
            return response.json()
        else:
            print(f"Failed to delete the Destination List {destination_list_id}. Status code: {response.status_code}, Response: {response.text}.")
    except Exception as e:
        print(f"An error occurred: {e}.")

def main():
    # Exit out if the required client_id or client_secret is not set
    for var in ['API_KEY', 'API_SECRET', 'OUTPUT_DIR']:
        if os.environ.get(var) == None:
            print("Required environment variable: {} not set".format(var))
            exit()

    # Get an API token
    api = API(token_url, client_id, client_secret)

    try:
        # Get the Destination Lists in the organization
        destination_list_ids = get_all_destination_lists(api, destination_lists_csv_file)

        # Create a Destination List, without destinations
        payload = {
            "access": "none",
            "isGlobal": False,
            "name": "name of a destination list",
            "bundleTypeId": 2
        }

        json_data = post_destination_lists(api, payload)

        # Get the ID of the created Destination List
        destination_list_id = json_data['data']['id'] if json_data else None
        json_data = get_destination_lists(api, destination_list_id)

        meta = json_data['data']['meta']
        destination_count = meta['destinationCount']
        print(f"The number of destinations in the destination list is {destination_count}.")

        # Update the name of the Destination List
        payload = {}
        payload['name'] = "new name of destination list"
        json_data = patch_destination_lists(api, destination_list_id, payload)

        # Add destinations to the Destination List
        destinations = [
            {
                "destination": "cisco.com",
                "type": "domain",
                "comment": "cisco ai defense"
            },
            {
                "destination": "openai.com",
                "type": "domain",
                "comment": "ai models"
            }
        ]
        json_data = post_destinations(api, destination_list_id, destinations)

        meta = json_data['data']['meta']
        destination_count = meta['destinationCount']
        print(f"After adding the destinations, the number of destinations in the destination list is {destination_count}.")

        # Get destinations in the Destination List
        destination_ids = get_destinations(api, destination_list_id, destinations_csv_file)

        # Remove destinations from the Destination List
        print(f"destinations: {destination_ids}")
        json_data = delete_destinations(api, destination_list_id, destination_ids)

        # Number of destinations in destination list after delete of destinations
        meta = json_data['data']['meta']
        destination_count = meta['destinationCount']
        print(f"The number of destinations in the destination list is {destination_count}.")

        # Delete the Destination List
        json_data = delete_destination_lists(api, destination_list_id)

    except Exception as e:
        print(e)

# main
if __name__ == "__main__":
    main()