Clubhouse is a remote company. Your own company may now also be one.Readourtipsontransitioningtoremotework

A non-engineer used our APIs and Zapier to build something awesome. How'd she do that?

Richard Huffaker

This is our first post in “How’d They Do That?”, a series where we’ll be looking at work done by both engineers and non-engineers to accomplish specific goals. Sometimes these posts will involve using Clubhouse, sometimes they won’t. We simply want to show you a useful thing and then walk you through how it was made so that you can potentially make a similar useful thing of your own.

Elizabeth Toy had a problem:

Our Zendesk integration wasn’t as robust as she and her team needed it to be.

Specifically, they couldn’t automatically generate or update Clubhouse Stories from within Zendesk Tickets, and, as you might expect, they also couldn’t do the reverse of this and automatically update Tickets from within Stories. The functionality that was there required manual selections to be made, and manual processes always have the potential for human error.

What was Elizabeth's solution?

Build something herself using our APIs and Zapier webhooks, creating her own more powerful integration.

Now, it’s possible that you may not be using Zendesk and so don’t particularly care about using Zendesk with Clubhouse. That’s totally reasonable. But many of the steps Elizabeth describes below can apply to using our APIs in many other situations.

We’ll let Elizabeth tell the story from here. And note that you can see her original version of this post on Medium.

Disclaimer: so I refer to myself as a non-engineer. And I am not one, in the truest sense. But I do work with a bunch of them and have learned enough to get by. I also took some classes in college but have not retained any of it, nor is it even something I’d use (VBA, ha).

I work on the Support team at Gloo. Our Engineering team decided to make the switch to Clubhouse (editor’s note: very smart and cool of them). It promised Zendesk integration—great! I didn’t have time to check it out in depth, but my manager told me to trust him, that it was better than our last system’s Zendesk integration.

Fast forward to all our engineers clamoring to use the new tool and wondering when we’d be sending bugs their way from Zendesk. Time to set this up. Should be easy right?

Nope. Wrong.

The integration consists of a Zendesk app. Nothing in Zendesk apps is trigger-able, action-able, filter-able, etc. in Zendesk or Zapier (the two tools I can usually count on to make things do what I want). We would have to manually select to add a card to Clubhouse. Manual processes have the potential for human error.

So I checked Zapier. Clubhouse has some actions available there (by invite). But no triggers (no bueno) and the only action is to Create a Story. So that was actually worse than our last system’s Zendesk integration.

What to do? APIs and Webhooks!

Zendesk→Clubhouse Zap

  1. Set the Zap’s trigger to be a new Zendesk ticket.
  2. Add an action using Formatter by Zapier to work with Text to Replace a specified value in a given input.
  3. In our case, I set the input to be the Zendesk ticket description and have it find quotation marks (") and replace them with \". (Do this if you don’t want errors later! If you have a quotation mark in your Webhook later, it’ll error.) Do this for any text that you’ll be sending into Clubhouse that could potentially have a quotation mark in it. I did it for the Zendesk ticket subject and description.
  4. I added some paths because there are different ticket conditions that would send it to different boards (like if the ticket is an issue on web, it goes to the Web team’s board). Set these up after the formatting actions if you need to send to different Clubhouse boards or do something similar.
  5. Add an action using Code by Zapier to Run Python.
  • Add one Input Data item called owner_email with the Assignee Email from the Zendesk ticket
  • The code will look something like this (please look at the screenshot below though to get the appropriate line indentations! Zapier is very particular about running this with the correct indentations):

    import argparse
    import requests
    session = requests.Session()
    session.header = {'Content-Type' : 'application/json'}
    get_members_url = "https://api.clubhouse.io/api/v2/members?token=
    [YourTokenHere]"
    members = []

    author = "
    [BackupAuthorIDHere]"

    while get_members_url:
    response = session.get(get_members_url)
    if response.status_code != 200:
    print('Failed to get members with error {}'.format(response.status_code))
    exit()
    members = response.json()
    for member in members:
    if member['profile']['email_address'] == input_data['owner_email']:
    author = member['id']
    return {
    'author' : author
    }

Replace any non-bold text above with your own inputs. (You need to generate a Clubhouse API Token.)

Screenshot of the Python code shown above in action

Python code to get Clubhouse member ID

5. Add an action using Webhooks by Zapier to fire a Custom Request.

  • The Method is POST.
  • The URL is https://api.clubhouse.io/api/v3/stories?token=[YourTokenHere]
  • The Data will look something like this:

    {
    "description": "
    Description of the card goes here. I used the Zendesk ticket description from step 1 / trigger.",
    "name": "
    Title of the card goes here. I used the Zendesk ticket subject from step 1 / trigger.",
    "project_id":
    1234,
    "story_type": "
    bug",
    "requested_by_id": "
    [author output from previous Python step]"
    "labels": [
    {"name": "
    label 1"},
    {"name": "
    label 2"},
    {"name": "
    label 3"}
    ],
    "external_id": "
    [zendeskTicketID]",
    "external_tickets": [{
    "external_id": "
    [zendeskTicketID]",
    "external_url": "
    https://[subdomain].zendesk.com/agent/tickets/[zendeskTicketID]"
    }]
    }

Replace any non-bold text above with your own inputs. Or check out Clubhouse’s Create Story API documentation for available parameters and make a custom request to fit your needs. To get the Project ID, I recommend a quick GET request using the List Projects API endpoint.

  • The Headers are Content-Type | application/json

6. Continue on through and test the Zap.

7. Add an action using Zendesk to Update a Ticket. Use the ticket ID from the Trigger step in the Zap and have it post a private note to say the card was added and include a link to the card.

Clubhouse→Zendesk Zap

1. Set the Zap’s trigger using Webhooks by Zapier and a Catch Hook trigger.

2. Add an action using Formatter by Zapier to work with Text to Split Text to separate the Actions data from the Webhook to new lines using [:newline:] with the Segment Index of All (as Separate Fields).

Text to split text in Zapier

3. Add a Filter step to only continue if the Actions data does not contain the string story-task.

4. Add an action using Formatter by Zapier to work with Text to Extract Pattern in a specified value in a given input.

Formatter by Zapier in action

5. Add a Filter step to only continue if Output from the previous step Exists.

An Only Continue If filter in Zapier

6. I set up different paths based on the type of update (Lane Changes, Comments, and Archived/Deleted) but for my own sanity, will only explain how I set up Comments. But if you want to do it similar to how I did, add your paths now.

7. Set up a filter (or path rule) to only continue if the Catch Hook “Actions” portion of the response contains entity_type: story-comment and the “Actionsdoes not contain action: delete

Setting up Catch Hook rules in Zapier

8. Add an action using Webhooks by Zapier to fire a Custom Request.

  • The method is GET.
  • The URL is https://api.clubhouse.io/api/v3/stories/[storyID]?token=[YourTokenHere]. Fill in the storyID from the previous Extract Pattern step (6) above.
  • The Headers are Content-Type | application/json

9. Add a Filter step to only continue if Output from the previous step Exists.

Another Only Continue If filter in Zapier

For an Archive/Delete path, you should omit steps 10/11 as it will error (there’s no card to get). And in the steps following, you would use the story ID rather than the external ID. (You could technically omit steps 10/11 for all paths, but the external ID is a more reliable source of the ID of the ticket to update.) Also for the Archive/Delete path, make sure to filter if the “Actions” portion of the response does not contain story-comment. Comment deletions would otherwise cause a false positive for a Story being deleted.

10. Add an action using Zendesk to Find a Ticket. For the Query, use the External ID item from the Custom Request in step 8.

For an Archive/Delete path, you would use the Query “https://app.clubhouse.io/[ClubhouseProject]/story/[storyID]” or, if you have a custom field to capture the URL (see step 6 in Zendesk→Clubhouse above) then you should use the Query fieldvalue:“https://app.clubhouse.io/[ClubhouseProject]/story/[storyID]”

11. Add a Filter step to only continue if ID of the ticket from the previous step Exists and the Status of our ticket from the previous step Does not exactly match Closed

Adding an extra filter step to the Only Continue If filter in Zapier

12. Add an action using Webhooks by Zapier to fire a Custom Request.

  • The method is GET.
  • The URL is https://api.clubhouse.io/api/v3/members/[memberID]?token=[YourTokenHere]. Fill in the memberID using the Catch Hook “Member ID” portion of the response.
  • The Headers are Content-Type | application/json

This gets the profile information for the commenter.

13. Add an action using Webhooks by Zapier to fire a Custom Request.

  • The method is GET.
  • The URL is https://api.clubhouse.io/api/v3/stories/[storyID]/comments/[commentID]?token=[YourTokenHere]. Fill in the storyID using the output from step 3. Fill in the commentID using the Catch Hook “Primary ID” portion of the response.
  • The Headers are Content-Type | application/json

This gets the comment information.

Custom request in Webhooks by Zapier

Have it post a private note that says something like this:

[commenterName] commented on Clubhouse card [storyID]: [commentText]

Fill in the commenterName using the Custom Request in step 12 “Profile Name” portion of the response.

Fill in the storyID using the output from step 3.

Fill in the commentText using the Custom Request in step 13 “Text” portion of the response.

If you made it to this point, kudos to you. What I explained above is a pretty specific use-case, but it’s meant to show you that with awesome APIs and a little webhook magic, you can do a whole lot.

Maybe you want to have comments on Clubhouse cards post directly to a Slack channel. Easy! Take the Clubhouse→Zendesk steps and omit step 5–7 and change step 11 to post to a Slack channel instead of updating a ticket.

Once you get one thing set up, you’ll realize all the data you have to work with and will want to keep going. Especially in Clubhouse’s case, with so many APIs available, you can GET and POST until you’re blue in the face.