Publishing Guru Cards to a Third-Party

This article shows you how to use Guru's SDK to publish cards from Guru to a third-party. We'll use an Intercom Help Center as the platform we're publishing to as an example.

If you'd like to jump right to our code, the entire script to publish to Intercom is available here. We also have examples that publish to Readme and Salesforce.

What we'll cover here

  • Understanding the nomenclature
  • Running the script
  • The Publisher class
  • Creating and updating Intercom articles
  • Finding Intercom Collections and Sections
  • How the script stores data
  • Publishing to other third-party platforms

Understanding the nomenclature

Our SDK refers to objects by their Guru names (cards, boards, etc.) and refers to the third-party as being "external". So when we say we'll publish a Guru Card to become an Intercom Article, the SDK refers to the Intercom Article as an "external card".

Intercom also has grouping structures called collections and sections. We can match Guru objects up to these structures. Here's an example:

Guru NameSDK Generic NameIntercom Name
Collectionexternal collectionCollection
Folderexternal sectionSection
Cardexternal cardArticle

Running the script

To run the script you'll need to set up a few environment variables:

Once you have those values you can run the intercom_publish.py script:

GURU_USER="[email protected]" GURU_API_TOKEN="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" \
  INTERCOM_API_TOKEN="dG9rOjEyMzRhYmNkLTU2NzgtYWJjZC1kZWZhLTEyMzQ1Njc4MTIzNDp2MS4wMA==" \
  python intercom_publish.py

The Publisher class

Guru's SDK provides a base Publisher class. This implements all the functionality you need on the Guru side. Here's what it does for you:

  • Enumerates the Board Groups, Boards, Sections, and Cards in a Collection.
  • Tracks object versions so it knows when a Card has changed and needs to be published.
  • Remembers the external ID of each object.
  • Knows whether an object has been published before, so it knows when to create vs. update the external object.
  • Finds links between Guru Cards and helps you convert them to be links between external articles.

To publish Cards to Intercom, we extend The SDK's Publisher class to create an IntercomPublisher class. The IntercomPublisher class is where we implement what's specific to Intercom, like the API calls to create and update Intercom articles.

To publish some content, we just need to tell it where to start. Here's an example:

import guru

g = guru.Guru()
publisher = IntercomPublisher(g)
publisher.publish_collection("Help Center")

That tells the SDK to enumerate all of the boards, sections, and cards in your Help Center collection and see which objects have changes that need to be published. When it finds an object that needs to be created or updated in Intercom, it'll call the method to make that update -- since these methods are Intercom-specific, they're the ones we'll have to implement in the IntercomPublisher class.

Creating and updating Intercom articles

When a Card needs to be published, one of two methods will be called:

  • create_external_card is called when the Card has never been published before and needs to be created in Intercom.
  • update_external_card is called when the Card has been published before and we need to update the Intercom article.

Here's how we implement these methods:

  def create_external_card(self, card, changes, section, board, board_group, collection):
    data = self.convert_card_to_article(card, section, board)
    url = "https://api.intercom.io/articles"
    
    # we return the Intercom article's ID.
    return requests.post(url, json=data, headers=self.get_headers()).json().get("id")
  
  def update_external_card(self, external_id, card, changes, section, board, board_group, collection):
    data = self.convert_card_to_article(card, section, board)
    url = "https://api.intercom.io/articles/%s" % external_id
   
    # this method returns the response object so the SDK will know
    # if the API call to update the article was successful.
    return requests.put(url, json=data, headers=self.get_headers())

One makes the POST call to create an Intercom article, the other makes the PUT call to update the article.

When a new Card is created, the SDK knows to call create_external_card. We return the Intercom article's ID and the SDK remembers it. When that Card is updated in Guru, the SDK will know to call update_external_card and it'll provide the Intercom article ID (as the external_id parameter).

To format the JSON payload for these calls, we define a method called convert_card_to_article, which converts a Guru Card object to the format Intercom expects. Here's how that's implemented:

  def convert_card_to_article(self, card, section, board):
    data = {
      "title": card.title,
      "author_id": 5056532,
      "body": card.content,
      "state": "published",
    }
    
    # if the card is on a section, that's its parent in Intercom.
    # if it's on a board but not a section, the board is its parent in Intercom.
    if section:
      data["parent_id"] = self.get_external_id(section.id)
      data["parent_type"] = "section"
    elif board:
      data["parent_id"] = self.get_external_id(board.id)
      data["parent_type"] = "collection"
   
    return data

Finding Intercom collections and sections

There are two ways the SDK can be aware of an external object:

  1. It created it and remembers its external ID.
  2. It checks for an existing object and finds one whose name matches.

Our sample Intercom publishing script does not create or update Intercom collections or sections, it simply expects they'll already exist. So, when the SDK sees a Board in Guru and doesn't know its Intercom ID, it calls the find_external_board method. This method gets a list of Intercom collections and checks if any match the name of our Board. Here's how that's implemented:

  def find_external_board(self, guru_board):
    intercom_collections = self.get_all("https://api.intercom.io/help_center/collections")
    
    for intercom_collection in intercom_collections:
      if intercom_collection["name"].lower() == guru_board.title.lower():
        return intercom_collection["id"]

When we create an article, we need to set its parent_id to be the Intercom ID of the collection or section and this is how we know that ID.

How the Script stores data

The script needs to remember some information from one run to the next, including:

  • What cards have been published.
  • Which version of each Card was published.
  • The Intercom article ID that corresponds to each Guru Card.
  • The Intercom IDs for other objects.

All of this metadata is stored in a JSON file called IntercomPublisher.json. It's written to the script's working directory. Make sure you don't lose this file -- if you do, the script won't know which Cards have been published before and may create duplicate articles.

Publishing to other third-party platforms

In addition to Intercom, we also have examples that publish to Readme and Salesforce. To publish to another platform not listed here, you'll need to figure out all the things we covered including:

  1. Which objects are being published and what they'll become. For example, does a Guru Board correspond to anything in the system you're publishing to?
  2. Implement the API calls to create and update objects in the other system.