Source code for salespyforce.knowledge

# -*- coding: utf-8 -*-
"""
:Module:            salespyforce.knowledge
:Synopsis:          Defines the Knowledge-related functions associated with the Salesforce API
:Created By:        Jeff Shurtliff
:Last Modified:     Jeff Shurtliff
:Modified Date:     03 Feb 2026
"""

from __future__ import annotations

from typing import Optional

from . import errors
from .utils import log_utils

# Define constants
KNOWLEDGE_SOBJECT = 'Knowledge__kav'

# Initialize logging
logger = log_utils.initialize_logging(__name__)


[docs] def check_for_existing_article( sfdc_object, title: str, sobject: Optional[str] = None, return_id: bool = False, return_id_and_number: bool = False, include_archived: bool = False, ): """This method checks to see if an article already exists with a given title and returns its article number. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_) .. versionchanged:: 1.2.2 You can now specify whether archived articles are included in the query results. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param title: The title of the knowledge article for which to check :type title: str :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param return_id: Determines if the Article ID should be returned (``False`` by default) :type return_id: bool :param return_id_and_number: Determines if Article ID and Article Number should be returned (``False`` by default) :type return_id_and_number: bool :param include_archived: Determines if archived articles should be included (``False`` by default) :type include_archived: bool :returns: The Article Number, Article ID, or both, if found, or a blank string if not found """ sobject = 'Knowledge__kav' if sobject is None else sobject query = f"SELECT Id,ArticleNumber FROM {sobject} WHERE Title = '{title}'" query += " AND PublishStatus != 'Archived'" if not include_archived else query response = sfdc_object.soql_query(query, replace_quotes=False) return_value = '' if response.get('totalSize') > 0: if return_id: return_value = response['records'][0]['Id'] elif return_id_and_number: return_value = (response['records'][0]['Id'], response['records'][0]['ArticleNumber']) else: return_value = response['records'][0]['ArticleNumber'] elif return_id_and_number: return_value = ('', '') return return_value
[docs] def get_article_id_from_number( sfdc_object, article_number, sobject: Optional[str] = None, return_uri: bool = False, ) -> str: """This method returns the Article ID when an article number is provided. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_) .. warning:: The ability to retrieve the article URI/URL rather than the ID will be moved to a separate function in a future release. .. versionchanged:: 1.4.0 A logic issue has been fixed and improved to make this function more robust and stable. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_number: The Article Number to query :type article_number: str, int :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param return_uri: Determines if the URI of the article should be returned rather than the ID (``False`` by default) :type return_uri: bool :returns: The Article ID or Article URI, or a blank string if no article is found :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError` """ # Ensure the sobject is defined appropriately if sobject and not isinstance(sobject, str): exc_msg = f'The sobject must be a string (provided: {type(sobject)})' logger.error(exc_msg) raise TypeError(exc_msg) if not sobject: sobject = KNOWLEDGE_SOBJECT logger.debug(f'The {KNOWLEDGE_SOBJECT} sObject will be used as one was not provided') # Construct the SOQL query to perform article_number = str(article_number) if not isinstance(article_number, str) else article_number if len(article_number) < 9: query = f"SELECT Id FROM {sobject} WHERE ArticleNumber LIKE '%0{article_number}'" else: query = f"SELECT Id FROM {sobject} WHERE ArticleNumber = '{article_number}'" # Perform the SOQL query and return the article number if found response = sfdc_object.soql_query(query) if response.get('totalSize') > 0: if return_uri: # TODO: Split out the return_uri functionality into a separate function and method warn_msg = ("The ability to retrieve the article URI/URL rather than the ID (return_uri parameter) will " "be moved to a separate function/method in a future release") logger.warning(warn_msg) errors.handlers.display_warning(warn_msg) return_value = response['records'][0]['attributes']['url'] else: return_value = response['records'][0]['Id'] else: return_value = '' warn_msg = f'No results were returned when querying for the article number {article_number}' logger.warning(warn_msg) return return_value
[docs] def get_articles_list( sfdc_object, query: Optional[str] = None, sort: Optional[str] = None, order: Optional[str] = None, page_size: int = 20, page_num: int = 1, ): """This function retrieves a list of knowledge articles. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artlist.htm>`_) .. versionchanged:: 1.4.0 The errors now log as errors via the logger rather than to the stderr console. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param query: A SOQL query with which to filter the results (optional) :type query: str, None :param sort: One of the following optional values: ``LastPublishedDate``, ``CreatedDate``, ``Title``, or ``ViewScore`` :type sort: str, None :param order: Optionally define the ORDER BY as ``ASC`` or ``DESC`` :type order: str, None :param page_size: The number of results per page (``20`` by default) :type page_size: int :param page_num: The starting page number (``1`` by default) :type page_num: int :returns: The list of retrieved knowledge articles """ # Define the headers headers = sfdc_object._get_headers('articles') # Validate the sort field # TODO: Convert list below into constant valid_sort_options = ['LastPublishedDate', 'CreatedDate', 'Title', 'ViewScore'] if sort and sort not in valid_sort_options: logger.error(f"The sort value '{sort}' is not valid and will be ignored") sort = None # Validate the order field if order and order.upper() not in ['ASC', 'DESC']: logger.error(f"The order value '{order}' is not valid and will be ignored") order = None # Validate the page size field if page_size > 100: logger.error('The pageSize value exceeds the maximum and will default to 100') page_size = 100 # Validate the pageNumber field if page_num < 1: logger.error('The pageNumber value is not valid and will default to 1') page_num = 1 # Add values to the parameters dictionary if they have been defined params = {} if query: params['q'] = query if sort: params['sort'] = sort if order: params['order'] = order params['pageSize'] = page_size params['pageNumber'] = page_num # Perform the query # TODO: Determine what is returned by this API call and see if data should be pruned to just the list of articles return sfdc_object.get(f'/services/data/{sfdc_object.version}/support/knowledgeArticles', params=params, headers=headers)
[docs] def get_article_details( sfdc_object, article_id: str, sobject: Optional[str] = None, use_knowledge_articles_endpoint: Optional[bool] = None, ): """This function retrieves details for a single knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_) .. versionchanged:: 1.4.0 A logic issue was resolved and the new optional ``use_knowledge_articles_endpoint`` parameter can now be set to force the ``knowledgeArticles`` endpoint to be used for the GET request rather than the ``sobjects`` endpoint. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID for which to retrieve details :type article_id: str :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param use_knowledge_articles_endpoint: Optionally use the ``knowledgeArticles`` endpoint rather than ``sobjects`` to retrieve the article details (``False`` by default) :type use_knowledge_articles_endpoint: bool, None :returns: The details for the knowledge article :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.DataMismatchError` """ # Define the headers based on the endpoint that will be utilized headers = sfdc_object._get_headers('articles') if use_knowledge_articles_endpoint else None # Ensure there are no conflicting parameters if sobject and use_knowledge_articles_endpoint: if sobject == KNOWLEDGE_SOBJECT: info_msg = (f'It is not necessary to define the sObject as {KNOWLEDGE_SOBJECT} when leveraging ' f'the knowledgeArticles endpoint') logger.info(info_msg) else: error_msg = 'You cannot use the knowledgeArticles endpoint with an explicitly defined sObject' logger.error(error_msg) raise errors.exceptions.DataMismatchError(error_msg) # Define the endpoint to use in the GET request if use_knowledge_articles_endpoint: endpoint = f'/services/data/{sfdc_object.version}/support/knowledgeArticles/{article_id}' else: sobject = KNOWLEDGE_SOBJECT if not sobject else sobject endpoint = f'/services/data/{sfdc_object.version}/sobjects/{sobject}/{article_id}' # Perform the query and return the data data = sfdc_object.get(endpoint, headers=headers) # TODO: Determine what is returned by this API call and see if data should be pruned to just the article details (for both endpoints) return data
[docs] def get_validation_status( sfdc_object, article_id: Optional[str] = None, article_details: Optional[dict] = None, sobject: Optional[str] = None, ) -> str: """This function retrieves the Validation Status for a given Article ID. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_) .. versionchanged:: 1.4.0 The function now returns an empty string rather than a ``None`` value if the ``ValidationStatus`` field is not found in the article details data, and a more specific exception class is used when input data is missing instead of the generic :py:exc:`RuntimeError` exception class. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID for which to retrieve details :type article_id: str, None :param article_details: The dictionary of article details for the given article :type article_details: dict, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :returns: The validation status as a text string :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.MissingRequiredDataError` """ if not any((article_id, article_details)): raise errors.exceptions.MissingRequiredDataError('The article ID or article details must be provided.') # Retrieve the article details if not already supplied if not article_details: article_details = get_article_details(sfdc_object, article_id, sobject) # Identify the validation status return article_details.get('ValidationStatus', '')
[docs] def get_article_metadata(sfdc_object, article_id: str): """This function retrieves metadata for a specific knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_metadata.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID for which to retrieve details :type article_id: str :returns: The article metadata as a dictionary :raises: :py:exc:`RuntimeError` """ return sfdc_object.get(f'/services/data/{sfdc_object.version}/knowledgeManagement/articles/{article_id}')
[docs] def get_article_version(sfdc_object, article_id: str): """This function retrieves the version ID for a given master article ID. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_version.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID for which to retrieve details :type article_id: str :returns: The version ID for the given master article ID :raises: :py:exc:`RuntimeError` """ endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleversions/masterVersions/{article_id}' # TODO: Determine what is returned by this API call and see if data should be pruned to just the Version ID return sfdc_object.get(endpoint)
[docs] def get_article_url( sfdc_object, article_id: Optional[str] = None, article_number=None, sobject: Optional[str] = None, ) -> str: """This function constructs the URL to view a knowledge article in Lightning or Classic. .. versionchanged:: 1.2.0 Changed when lightning URLs are defined and fixed an issue with extraneous slashes. :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID for which to retrieve details :type article_id: str, None :param article_number: The article number for which to retrieve details :type article_number: str, int, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :returns: The article URL as a string :raises: :py:exc:`ValueError` """ sobject = 'Knowledge__kav' if sobject is None else sobject if not any((article_id, article_number)): raise ValueError('An article ID or an article number must be provided to retrieve the article URL.') if article_number and not article_id: article_id = get_article_id_from_number(sfdc_object, article_number, sobject) segment = '' if sfdc_object.base_url.endswith('/') else '/' if 'lightning' in sfdc_object.base_url or sobject == 'Knowledge__kav': article_url = f'{sfdc_object.base_url}{segment}lightning/r/Knowledge__kav/{article_id}/view' else: article_url = f'{sfdc_object.base_url}{segment}knowledge/publishing/articleDraftDetail.apexp?id={article_id}' return article_url
[docs] def create_article( sfdc_object, article_data: dict, sobject: Optional[str] = None, full_response: bool = False, ): """This function creates a new knowledge article draft. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_data: The article data used to populate the article :type article_data: dict :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default) :type full_response: bool :returns: The API response or the ID of the article draft :raises: :py:exc:`ValueError`, :py:exc:`TypeError`, :py:exc:`RuntimeError` """ # Get the appropriate sObject to call sobject = 'Knowledge__kav' if sobject is None else sobject # Ensure the payload is in the appropriate format if not isinstance(article_data, dict): raise TypeError('The article data must be provided as a dictionary.') # Ensure that the required fields have been provided required_fields = ['Title', 'UrlName'] for field in required_fields: if field not in article_data: raise ValueError(f'The following required field is missing from the article data: {field}') # Perform the API call response = sfdc_object.post(f'/services/data/{sfdc_object.version}/sobjects/{sobject}', payload=article_data) # Return the full response or just the article ID if not full_response: response = response.get('id') return response
[docs] def update_article( sfdc_object, record_id: str, article_data: dict, sobject: Optional[str] = None, include_status_code: bool = False, ): """This function updates an existing knowledge article draft. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param record_id: The ID of the article draft record to be updated :type record_id: str :param article_data: The article data used to update the article :type article_data: dict :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param include_status_code: Determines if the API response status code should be returned (``False`` by default) :type include_status_code: bool :returns: A Boolean indicating if the update operation was successful, and optionally the API response status code :raises: :py:exc:`ValueError`, :py:exc:`TypeError`, :py:exc:`RuntimeError` """ # Get the appropriate sObject to call sobject = 'Knowledge__kav' if sobject is None else sobject # Ensure the payload is in the appropriate format if not isinstance(article_data, dict): raise TypeError('The article data must be provided as a dictionary.') # Ensure that the required fields have been provided required_fields = ['Title', 'UrlName'] for field in required_fields: if field not in article_data: raise ValueError(f'The following required field is missing from the article data: {field}') # Perform the API call response = sfdc_object.patch(f'/services/data/{sfdc_object.version}/sobjects/{sobject}/{record_id}', payload=article_data) # Determine whether the call was successful successful = True if response.status_code == 204 else False # Return the success determination and optionally the status code if include_status_code: return successful, response.status_code return successful
[docs] def create_draft_from_online_article(sfdc_object, article_id: str, unpublish: bool = False): """This function creates a draft knowledge article from an online article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#createDraftFromOnlineKnowledgeArticle>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The ID of the online article from which to create the draft :type article_id: str :param unpublish: Determines if the online article should be unpublished when the draft is created (``False`` by default) :type unpublish: bool :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ # Define the payload for the API call payload = { "inputs": [ { "action": "EDIT_AS_DRAFT_ARTICLE", "unpublish": unpublish, "articleId": f"{article_id}" } ] } # Perform the API call endpoint = f'/services/data/{sfdc_object.version}/actions/standard/createDraftFromOnlineKnowledgeArticle' return sfdc_object.post(endpoint, payload)
[docs] def create_draft_from_master_version( sfdc_object, article_id: Optional[str] = None, knowledge_article_id: Optional[str] = None, article_data: Optional[dict] = None, sobject: Optional[str] = None, full_response: bool = False, ): """This function creates an online version of a master article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.198.0.knowledge_dev.meta/knowledge_dev/knowledge_REST_edit_online_master.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID from which to create the draft :type article_id: str, None :param knowledge_article_id: The Knowledge Article ID (``KnowledgeArticleId``) from which to create the draft :type knowledge_article_id: str, None :param article_data: The article data associated with the article from which to create the draft :type article_data: dict, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default) :type full_response: bool :returns: The API response or the ID of the article draft :raises: :py:exc:`RuntimeError` """ if not any((article_id, knowledge_article_id, article_data)): # TODO: Change to more specific exception class (errors.exceptions.MissingRequiredDataError) raise RuntimeError('Need to provide article ID, knowledge article ID, or article data') # Get the appropriate sObject to call sobject = KNOWLEDGE_SOBJECT if sobject is None else sobject # Get the knowledge article ID as needed if not knowledge_article_id: if not article_data: article_data = sfdc_object.get_article_details(article_id, sobject=sobject) knowledge_article_id = article_data.get('KnowledgeArticleId') # Perform the API call to retrieve the new draft ID endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleVersions/masterVersions' response = sfdc_object.post(endpoint, {'articleId': knowledge_article_id}) # Return the full response or the draft ID if not full_response: response = response.get('id') return response
[docs] def publish_article( sfdc_object, article_id: str, major_version: bool = True, full_response: bool = False, ): """This function publishes a draft knowledge article as a major or minor version. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_publish_master_version.htm>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The Article ID to publish :type article_id: str :param major_version: Determines if the published article should be a major version (``True`` by default) :type major_version: bool :param full_response: Determines if the full API response should be returned (``False`` by default) :type full_response: bool :returns: A Boolean value indicating the success of the action or the API response from the PATCH request :raises: :py:exc:`RuntimeError` """ # Define the payload for the API call payload = { "publishStatus": "Online" } if major_version: payload['versionNumber'] = 'NextVersion' # Perform the API call endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleVersions/masterVersions/{article_id}' result = sfdc_object.patch(endpoint, payload) # Return the appropriate value depending on if a full response was requested if not full_response: result = True if result.status_code == 204 else False return result
[docs] def publish_multiple_articles(sfdc_object, article_id_list: list, major_version: bool = True): """This function publishes multiple knowledge article drafts at one time. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#publishKnowledgeArticles>`_) :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id_list: A list of Article IDs to be published :type article_id_list: list :param major_version: Determines if the published article should be a major version (``True`` by default) :type major_version: bool :returns: The API response from the POST request :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError`, :py:exc:`ValueError` """ # Define the endpoint URI endpoint = f'/services/data/{sfdc_object.version}/actions/standard/publishKnowledgeArticles' # Ensure there is at least one article ID to publish if not isinstance(article_id_list, list) or not isinstance(article_id_list[0], str): raise TypeError('A list of Article ID strings must be provided in order to publish multiple articles.') elif len(article_id_list) == 0: # TODO: Change to more specific exception class (errors.exceptions.MissingRequiredDataError) raise ValueError('No article ID strings were found in the article ID list variable.') # Define the action to perform action = 'PUBLISH_ARTICLE_NEW_VERSION' if major_version else 'PUBLISH_ARTICLE' # Construct the payload payload = { "inputs": [ { "articleVersionIdList": article_id_list, "pubAction": action } ] } # Perform the API call return sfdc_object.post(endpoint, payload)
[docs] def assign_data_category(sfdc_object, article_id: str, category_group_name: str, category_name: str): """This function assigns a single data category for a knowledge article. (`Reference <https://itsmemohit.medium.com/quick-win-15-salesforce-knowledge-rest-apis-bb0725b2040e>`_) .. versionadded:: 1.2.0 :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The ID of the article to update :type article_id: str :param category_group_name: The unique Data Category Group Name :type category_group_name: str :param category_name: The unique Data Category Name :type category_name: str :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ # Define the payload for the API call payload = { "ParentId": article_id, "DataCategoryGroupName": category_group_name, "DataCategoryName": category_name } # Perform the API call endpoint = f'/services/data/{sfdc_object.version}/sobjects/Knowledge__DataCategorySelection' return sfdc_object.post(endpoint, payload)
[docs] def archive_article(sfdc_object, article_id: str): """This function archives a published knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_archive_master_version.htm>`_) .. versionadded:: 1.3.0 :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param article_id: The ID of the article to archive :type article_id: str :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ # Define the payload for the API call payload = { "publishStatus": "Archived" } # Perform the API call endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleVersions/masterVersions/{article_id}' return sfdc_object.patch(endpoint, payload)
[docs] def delete_article_draft(sfdc_object, version_id: str, use_knowledge_management_endpoint: bool = True): """This function deletes an unpublished knowledge article draft. .. versionadded:: 1.4.0 :param sfdc_object: The instantiated SalesPyForce object :type sfdc_object: class[salespyforce.Salesforce] :param version_id: The 15-character or 18-character ``Id`` (Knowledge Article Version ID) value :type version_id: str :param use_knowledge_management_endpoint: Leverage the ``/knowledgeManagement/articleVersions/masterVersions/`` endpoint rather than the ``/sobjects/Knowledge__kav/`` endpoint (``True`` by default) :type use_knowledge_management_endpoint: bool :returns: The API response from the DELETE request :raises: :py:exc:`RuntimeError` """ if use_knowledge_management_endpoint: endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleVersions/masterVersions/{version_id}' else: endpoint = f'/services/data/{sfdc_object.version}/sobjects/Knowledge__kav/{version_id}' return sfdc_object.delete(endpoint)