Zenaton white logo

API connectors

Overview

Why connectors?

Using third party APIs often requires some boilerplate like:

  • Managing credentials
  • Providing an authentication flow for your users (OAuth)
  • Handling events via webhooks
  • Coding tasks to make API calls

Using connectors, you let Zenaton handle all of this for you so you can focus on what matter: your business workflow.

Use connectors

Adding a connector

Step 1: Access to the Zenaton Dashboard

Check the Connectors tab in the dashboard and click on the third party service you want.

Stacktrace detail async jobs

Step 2: Connector Authentification

First, create a name for your connector name. The name purpose is only to help you differentiate multiple connectors of the same service.

Then, depending on the connector authentication method, you have to additionally provide the following informations:

  • OAuth2: Client ID and Client Secret
  • OAuth1: Consumer key and Consumer Secret
  • Basic: Username and Password
  • Api key: API Key

Stacktrace detail async jobs

Step 3 (for OAuth2 and OAuth1 only)

Provide this callback URL on your third party OAuth application: https://int.bearer.sh/v2/auth/callback
Then click on the connect button to generate your auth_id.

Making API calls with a connector

To make an API call with your connector you have to provide both the service_id and the connector_id to the connector method of your workflow. This information is available on the Zenaton Dashboard.

const gmail = this.connector("gmail", "CONNECTOR_ID");

yield gmail.get("users/me/messages");

Add your own connector

If you need to interact with an API for which we do not have a connector yet or because it's one of your internal APIs.
It's easy as wrap the logic into a Zenaton task skeleton. So then you can re-use it in many places.

In our example, we will create a simple Task to send Slack message using the Slack SDK.
If the API does not have an SDK, then you would need to add the http calls to the API in your task.

Here are the steps we will follow:

  • 1. Create a Zenaton task file
  • 2. Write your API calls inside the task file
  • 3. Then add your API credentials
  • 4. Run the task

Lets walk through an example of creating a task to the the Slack API so that we can send a message to slack using the Slack SDK.

1. Create a Zenaton task file

Add a Zenaton task boilerplate, into a file: src/Tasks/SlackTask.phpsrc/Tasks/slack_task.rbtasks/slack_task.pysrc/Tasks/SlackTask.jssrc/Tasks/SlackTask.js

<?php
    
use Zenaton\Interfaces\TaskInterface;
use Zenaton\Traits\Zenatonable;

class SlackTask implements TaskInterface
{
    use Zenatonable;

    public function handle()
    {
        ... // task implementation
    }
}
require 'zenaton'

class SlackTask < Zenaton::Interfaces::Task
  include Zenaton::Traits::Zenatonable

  def handle
    # task implementation
  end
end
from zenaton.abstracts.task import Task
from zenaton.traits.zenatonable import Zenatonable

class SlackTask(Task, Zenatonable):

    def handle(self):
        # Your task implementation
const { Task } = require("zenaton");

module.exports = Task("SlackTask", {
  async handle() {
    // [...] task implementation
  }
});
const { task } = require("zenaton");

module.exports = task("SlackTask", {
  async handle() {
    // [...] task implementation
  }
});

2. Write your API calls inside the task file

We will use the Slack SDK, so we will need to add it to our codebase dependency.

npm install @slack/web-api
npm install @slack/web-api
pip3 install slackclient
composer require php-http/curl-client:"^1.7" guzzlehttp/psr7
composer require jolicode/slack-php-api

From their docdocdocdocdoc , we simply copy their snippet:

const { WebClient } = require('@slack/web-api');

// Read a token from the environment variables
const token = process.env.SLACK_TOKEN;

// Initialize
const web = new WebClient(token);

// Call a method
await web.chat.postMessage({
  text: 'Hello world!',
  channel: conversationId,
});
const { WebClient } = require('@slack/web-api');

// Read a token from the environment variables
const token = process.env.SLACK_TOKEN;

// Initialize
const web = new WebClient(token);

// Call a method
await web.chat.postMessage({
  text: 'Hello world!',
  channel: conversationId,
});
import os
import slack

client = slack.WebClient(token=os.environ['SLACK_API_TOKEN'])

response = client.chat_postMessage(channel='#random', text="Hello world!")
use JoliCode\Slack\ClientFactory;

$client = ClientFactory::create('Your token');
// This method require your token to have the scope "chat:write"
$result = $client->chatPostMessage([
    'username' => 'example bot',
    'channel' => 'general',
    'text' => 'Hello world',
]);
require 'slack-ruby-client'

Slack.configure do |config|
  config.token = ENV['SLACK_API_TOKEN']
  raise 'Missing ENV[SLACK_API_TOKEN]!' unless config.token
end

client = Slack::Web::Client.new

client.auth_test

client.chat_postMessage(channel: '#general', text: 'Hello World', as_user: true)
            

And turn it into a our new Zenaton task by integrating the snippet into your task file.

The final code is:

const { Task } = require("zenaton");

const { WebClient } = require('@slack/web-api');
const token = process.env.SLACK_TOKEN;
const web = new WebClient(token);

module.exports = Task("SlackTask", {
  async handle() {
    await web.chat.postMessage({
      text: 'Hello from a Zenaton task!',
      channel: 'my-channel',
    });
  }
});
import os
import slack
    
from zenaton.abstracts.task import Task
from zenaton.traits.zenatonable import Zenatonable

class SlackTask(Task, Zenatonable):

    def handle(self):
        client = slack.WebClient(token=os.environ['SLACK_TOKEN'])
        client.chat_postMessage(channel='#my-channel', text="Hello from a Zenaton task!")
const { task } = require("zenaton");

const { WebClient } = require('@slack/web-api');
const token = process.env.SLACK_TOKEN;
const web = new WebClient(token);

module.exports = task("SlackTask", {
  async handle() {
    await web.chat.postMessage({
      text: 'Hello from a Zenaton task!',
      channel: 'my-channel',
    });
  }
});
<?php
    
use Zenaton\Interfaces\TaskInterface;
use Zenaton\Traits\Zenatonable;

use JoliCode\Slack\ClientFactory;

class SlackTask implements TaskInterface
{
    use Zenatonable;

    public function handle()
    {
        $token = $_ENV["SLACK_TOKEN"];
        $client = ClientFactory::create($token);
        $client->chatPostMessage([
            'channel' => 'my-channel',
            'text' => 'Hello from a Zenaton task!',
        ]);
    }
}
require 'slack-ruby-client'
# :nodoc:
class SlackTask < Zenaton::Interfaces::Task
  include Zenaton::Traits::Zenatonable

  def handle
    Slack.configure do |config|
        config.token = ENV['SLACK_TOKEN']
        raise 'Missing ENV[SLACK_TOKEN]!' unless config.token
    end

    client = Slack::Web::Client.new
    client.auth_test
    client.chat_postMessage(channel: 'my-channel', text: 'Hello from a Zenaton task!')
  end
end

3. Then add your API credentials

We will add a SLACK_TOKEN environment variable to the .env file used by our agent.
Note that we will need to unlisten and then listen to take this new parameter into account.

ZENATON_APP_ID=XXXXXX
ZENATON_API_TOKEN=XXXXXXXXXXXXXXXXXXXX
ZENATON_APP_ENV=dev

SLACK_TOKEN=xoxp-xxxxxxxx

4. Run the task

Now we can test our task by running this launch script launch_slack_task.js

require("./client");

const SlackTask = require("./Tasks/SlackTask");

new SlackTask().dispatch();
const { run } = require("./client");

run.task("SlackTask");
import client
from tasks.slack_task import SlackTask

SlackTask().dispatch()
#!/usr/bin/env php
<?php

require_once __DIR__.'/../src/bootstrap.php';

(new SlackTask())->dispatch();
require './client'
require './tasks/slack_task'

SlackTask.new.dispatch

Update your boot file: boot.jsboot.jsboot.rbboot.rb

...
require("./Tasks/SlackTask");
...
require("./Tasks/SlackTask");
...
from tasks.slack_task import SlackTask
...
require './tasks/slack_task'

Then run the task.

node launch_slack_task.js

To make it more reusable, we should add some parameters to our SlackTask so that we can customize the slack message and the channel.

...

module.exports = Task("SlackTask", {
  init (text, channel) {
    this.text = text;
    this.channel = channel;
  },
  async handle() {
    await web.chat.postMessage({
      text: this.text,
      channel: this.channel,
    });
  }
});
...

module.exports = task("SlackTask", {
  async handle(text, channel) {
    await web.chat.postMessage({
      text: text,
      channel: channel,
    });
  }
});
...
                
class SlackTask(Task, Zenatonable):
    def __init__(self, text, channel):
        self.text = text
        self.channel = channel

    def handle(self):
        client = slack.WebClient(token=os.environ['SLACK_TOKEN'])
        client.chat_postMessage(channel=self.channel, text=self.text)
<?php
    
use Zenaton\Interfaces\TaskInterface;
use Zenaton\Traits\Zenatonable;

use JoliCode\Slack\ClientFactory;

class SlackTask implements TaskInterface
{
    use Zenatonable;

    public function __construct($text, $channel) {
        $this->text = $text;
        $this->channel = $channel;
    }

    public function handle()
    {
        $token = $_ENV["SLACK_TOKEN"];
        $client = ClientFactory::create($token);
        $client->chatPostMessage([
            'channel' => $this->channel,
            'text' => $this->text,
        ]);
    }
}
require 'slack-ruby-client'
# :nodoc:
class SlackTask < Zenaton::Interfaces::Task
  include Zenaton::Traits::Zenatonable

  def initialize(text, channel)
    @text = text
    @channel = channel
  end

  def handle
    Slack.configure do |config|
        config.token = ENV['SLACK_TOKEN']
        raise 'Missing ENV[SLACK_TOKEN]!' unless config.token
    end

    client = Slack::Web::Client.new
    client.auth_test
    client.chat_postMessage(channel: @channel, text: @text)
  end
end

So, now we can use it this way:

new SlackTask('Hello from My SlackTask', 'my-channel').dispatch();
run.task("SlackTask", 'Hello from My SlackTask', 'my-channel');
SlackTask("Hello from My SlackTask", "my-channel").dispatch()
(new SlackTask("Hello from a Zenaton task!" ,"my-channel"))->dispatch();
SlackTask.new("Hello from a Zenaton task!", "my-channel").dispatch

We can update our launch_slack_task.js to test the task:

require("./client");

const SlackTask = require("./Tasks/SlackTask");

new SlackTask('Hello from My SlackTask', 'my-channel').dispatch();
const { run } = require("./client");

run.task("SlackTask", 'Hello from My SlackTask', 'my-channel');
import client
from tasks.slack_task import SlackTask

SlackTask("Hello from My SlackTask", "my-channel").dispatch()
#!/usr/bin/env php
<?php

require_once __DIR__.'/../src/bootstrap.php';

(new SlackTask("Hello from a Zenaton task!" ,"my-channel"))->dispatch();
require './client'
require './tasks/slack_task'

SlackTask.new("Hello from a Zenaton task!", "my-channel").dispatch

Then run the task
We can now re-use this task in many of our workflows !

What's next?

Automatic retry — As APIs may have downtime, we could add an automatic retry to our API call so that it will automatically try again if it fails to connect.

Scheduling — And, if we plan to execute our API call on a recurring basis, we could set up a schedule for it using cron expressions.

`