AnyRobot
  • Overview
  • Core Concepts
  • Getting Started
  • Portal
    • Portal API
    • Admin
  • Runner
    • Runner API
    • Admin
  • Developing Robots
  • Troubleshooting
Powered by GitBook
On this page
  • Incoming Webhooks
  • Creating webhook
  • Running webhook
  • Handling webhook
  • Attachments
  • Text
  • HTML
  • File
  • Table
  • KPI
  • Job results
  • Update
  • Success
  • Failure
  • Assist Request
  • Text input
  • Yes/No
  • Radio buttons
  • Checkboxes
  • List
  1. Portal

Portal API

PreviousPortalNextAdmin

Last updated 2 years ago

Incoming Webhooks

The request shown here is used to start tasks from external systems.

Its operation proceeds as follows. The portal receives the webhook after which it passes it to the job which the runner retrieves, as the webhook was passed from the very beginning therefore the executed script has access to the data passed by the webhook.

Creating webhook

To create a webhook you must edit the workflow in which you want to add it. Once there, you need to find the triggers section and then select Add trigger -> Webhook there.

Then you enter the name of the webhook you are creating and any parameters if you need them. Once you have done that click save, and the webhook has been created!

Running webhook

After creating the webhook, you may notice that a link has been generated that you will use to run the job from an external source.

To run the webhook send a request to this address, if you want you can also add various parameters that will appear in the body of the job but it is not required. Below is shown an example in which one parameter was sent.

# Request

POST {portal address}/webhooks/triggers/{id}/run?hello=world 
# your generated webhook address goes here and should look like this

# Response

{
    "type": "job",
    "id": "1c6184bd-c49a-40bc-ac64-c2e0d84a2122",
    "workflow": {
        "id": "dbb4677b-acb9-4298-8578-ca612e24f22f",
        "name": "test_workflow-rb",
        "parameters": {
            "workflow": "test" // workflow parameters
        }
    },
    "project": {
        "id": null,
        "name": null
    },
    "trigger": {
        "id": null,
        "name": "webhook",
        "parameters": {
            "webhook": "params" // webhook parameters
        },
        "webhook": {
            "parameters": {
                "parsed_body": {
                    "body": "postman" // body sent in a request
                },
                "parsed_query": {
                    "params": "postman" // params sent in a request
                }
            }
        },
        "request_details": {
            "ip": "192.168.1.1",
            "timestamp": "2022-12-14T16:40:03.991+01:00",
            "request_method": "POST",
            "media_type": "text/plain",
            "http_headers": {
                "User-Agent": "PostmanRuntime/7.30.0",
                "Postman-Token": "5ee451ea-b1a7-43a7-884c-d37c03b9b832",
                "Accept-Encoding": "gzip, deflate, br",
                "Accept": "*/*",
                "Host": "sandbox.clients.anyrobot.cloud",
                "Version": "HTTP/1.1"
            },
            "original_url": "https://sandbox.clients.anyrobot.cloud/webhooks/triggers/33b53c6d-f0d2-4f37-b034-41f5ae999734/run?test=postman",
            "form_data": false,
            "raw_post": "{\r\n    \"body\": \"postman\"\r\n}",
            "server": "sandbox.clients.anyrobot.cloud",
            "all_headers": {
                "REQUEST_URI": "/webhooks/triggers/33b53c6d-f0d2-4f37-b034-41f5ae999734/run?test=postman",
                "PATH_INFO": "/webhooks/triggers/33b53c6d-f0d2-4f37-b034-41f5ae999734/run",
                "SCRIPT_NAME": "",
                "QUERY_STRING": "test=postman",
                "REQUEST_METHOD": "POST",
                "REMOTE_ADDR": "192.168.1.1",
                "REMOTE_PORT": "14641",
                "CONTENT_TYPE": "text/plain",
                "CONTENT_LENGTH": "27",
                "HTTPS": "on",
                "HTTP_USER_AGENT": "PostmanRuntime/7.30.0",
                "HTTP_POSTMAN_TOKEN": "5ee451ea-b1a7-43a7-884c-d37c03b9b832",
                "HTTP_ACCEPT_ENCODING": "gzip, deflate, br",
                "HTTP_ACCEPT": "*/*",
                "HTTP_HOST": "sandbox.clients.anyrobot.cloud",
                "HTTP_VERSION": "HTTP/1.1",
                "ORIGINAL_FULLPATH": "/webhooks/triggers/33b53c6d-f0d2-4f37-b034-41f5ae999734/run?test=postman",
                "ORIGINAL_SCRIPT_NAME": "",
                "RAW_POST_DATA": "{\r\n    \"body\": \"postman\"\r\n}"
            }
        }
    },
    "results_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/1c6184bd-c49a-40bc-ac64-c2e0d84a2122/results",
    "live_logging_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/1c6184bd-c49a-40bc-ac64-c2e0d84a2122/output",
    "assist_requests_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/1c6184bd-c49a-40bc-ac64-c2e0d84a2122/assist_requests"
}

Handling webhook

Below is a small example of how to access the payload and what a sample payload looks like with the parameters sent in the webhook:

#!/usr/bin/env ruby
require 'json'
require 'ostruct'

file_path = ENV["ANYROBOT_PAYLOAD_FILE"]
file = File.open (file_path)
json = OpenStruct.new(JSON.parse(file.read))
{
    "parameters": "{\"webhook\":\"params\"}",
    "job_id": "e3529122-4d79-426b-a652-d2b73060e897",
    "workflow_id": "dbb4677b-acb9-4298-8578-ca612e24f22f",
    "workflow_name": "test_workflow-rb",
    "trigger_name": null,
    "trigger_id": null,
    "secret": "69bbc6d2-61b3-475c-8d38-49d1c02c2008",
    "id": "e3529122-4d79-426b-a652-d2b73060e897",
    "workflow": {
        "id": "dbb4677b-acb9-4298-8578-ca612e24f22f",
        "name": "test_workflow-rb",
        "parameters": {
            "workflow": "test" // workflow parameters
        }
    },
    "trigger": {
        "id": null,
        "name": null,
        "parameters": {
            "webhook": "params" // webhook parameters
        },
        "webhook": {
            "parameters": {
                "parsed_body": {
                    "body": "postman" // data sent in body
                },
                "parsed_query": {
                    "test": "postman" // data sent as params
                }
            }
        },
        "request_details": {
            "ip": "192.168.1.1",
            "server": "sandbox.clients.anyrobot.cloud",
            "raw_post": "{\r\n    \"body\": \"postman\"\r\n}",
            "form_data": false,
            "timestamp": "2022-12-14T17:32:47.004+01:00",
            "media_type": "text/plain",
            "all_headers": {
                ...
            },
            "http_headers": {
                ...
            },
            "original_url": "https://sandbox.clients.anyrobot.cloud/webhooks/triggers/33b53c6d-f0d2-4f37-b034-41f5ae999734/run?test=postman",
            "request_method": "POST"
        }
    },
    "bot": {
        "id": "ad48bdf8-79d5-484d-8949-9d4f8f61e99a",
        "name": "Windows lukasz.b",
        "secret": "69bbc6d2-61b3-475c-8d38-49d1c02c2008"
    },
    "project": {
        "id": null,
        "name": null
    },
    "task": {
        "method": "download",
        "task_name": null,
        "branch_name": null,
        "download_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/e3529122-4d79-426b-a652-d2b73060e897/script"
    },
    "results_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/e3529122-4d79-426b-a652-d2b73060e897/results",
    "live_logging_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/e3529122-4d79-426b-a652-d2b73060e897/output",
    "assist_requests_url": "https://sandbox.clients.anyrobot.cloud/api/v1/jobs/e3529122-4d79-426b-a652-d2b73060e897/assist_requests"
}

Attachments

Attachments are elements that mainly appear in Job Results and Assist Requests.

They come in several types, but the fields are always similar:

  • genre - a type of attachment.

  • name - the name of the attachment.

  • code - code that is url-friendly - used to group the same attachments in the electrical_parameters_voltage series

  • body - the body of the attachment, varies depending on the type

Currently available are the types listed below.

Text

Text object, displayed in an iframe in Portal.

{
  "genre": "text",
  "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "Lorem Ipsum",
  "code": "electrical_parameters_voltage",
  "body": "Lorem ipsum dolor amet..."
}

HTML

HTML object, displayed in an iframe in Portal.

{
  "genre": "html",
  "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "test.html",
  "code": "electrical_parameters_voltage",
  "body": "<p>ok</p>"
}

File

data:<content-type>;base65,<base64 encoded content>

{
  "genre": "file",
  "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "list.png",
  "code": "electrical_parameters_voltage",
  "body": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
}

Table

Data in the table is encoded in base64 format, similar to the example above, the uploaded file is a CSV file, and we also support markdowns.

{
  "genre": "table",
  "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "xls.csv",
  "code": "electrical_parameters_voltage",
  "body": "a,b,c;1,2,3" //"text/csv;base64,(...)"
}

KPI

Your key performance indicator.

{
  "genre": "kpi",
  "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "stats.kpi",
  "code": "electrical_parameters_voltage",
  "body": {
    "genre": "integer",
    "value": "123",
    "target": "1000"
  }
}

Job results

It is a request to change the status of a job.

Their structure is as follows:

  • genre - success or failure is sent at the end of the process, update while the script is running.

  • code - here Linux-standard - the error code returned by the script, 0 when success, anything over when not.

  • result - what the script threw out.

  • attachments - according to the documentation here Attachments.

Currently, we can change the work status to 3 different types, and these are:

Update

Such information comes out from the bot during Job execution, can come down many times and we display it in the user panel.

# Request

POST /api/v1/jobs/94916326-9344-43f5-9187-1aea2931eac7/results

# Response

{
  "genre": "update",
  "result": {
    "lorem": "ipsum",
    "hello": "world"
  }, // result object, any json
  "attachments": [
    (...) // check the Attachment docs
  ]
}

Success

# Request

POST /api/v1/jobs/94916326-9344-43f5-9187-1aea2931eac7/results

# Response

{
  "genre": "success",
  "code": 0, // code (shell) returned by the process
  "result": {
    "lorem": "ipsum",
    "hello": "world"
  }, // result object, any json
  "attachments": [
    (...) // check the Attachment docs
  ]
}

Failure

# Request

POST /api/v1/jobs/94916326-9344-43f5-9187-1aea2931eac7/results

# Response

{
  "genre": "failure",
  "code": 127, // code (shell) returned by the process
  "result": {
    "lorem": "ipsum",
    "hello": "world"
  }, // result object, any json
  "attachments": [
    (...) // check the Attachment docs
  ]
}

Assist Request

In some advanced cases, human intervention may be needed, and for these cases, we have this request that allows you to ask several types of questions.

The basic request should look like this, the different types will be described below.

# Request

POST /api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/assist_requests

{
  "expires_at": "YYYY-MM-DD HH:MM:SS", // until when the request will be valid
  "question": "Is it assisted already?", // question content
  "attachments": [],
  "genre": "input", // type of assist request
  (...) // here are the other parameters of the assist that differs by type
}

# Response
// Created
{
  "status": "201 Created",
  "id": "63be582f-d330-44ec-943b-8179e1b28c9a",
  "url": "https://serwer.xxx.yyy.com/api/v1/assist_requests/d7dd76a0-b7a5-44d7-b98f-8712aa0e5505"
}

// Not responded yet
{
  "status": "200 OK",
  "time_left": 213213214 // in seconds or null when undefined - the amount of time left for the operator to respond
}

// Time Expired
{
  "status": "410 Gone",
  "time_left": 0
}

// Responded
{
  "id": "xxxxxx-xxxxxx-xxxxx-xxxx", // response id
  "responded_at": "YYYY-MM-DD HH:MM:SS", // when responded
  // data of the user who made the reply
  "user": {
    "id": 123,
    "email": "jan.kowalski@unitedideas.pl",
    "full_name": "Jan Kowalski"
  },
  // response data that differs by type
  (...)
}

Text input

{
  (...) // standard request part
  "genre": "input", // request type
  "required": true, // is it required
  "confirm": true, // is it necessary to confirm the selection in the interface?
  "short": true, // when true then input, when false then textarea
  "name": "login", // field name, reference for future
  "default": "blablabla" // pre-populate interface with this answer, non-mandatory
  "validate": "/\A[^.]+\.[^.]+\Z/" # valid javascript regex to validate answer
  "error": "Login may contain only letters and saves" # validation alert if not matches
}

# Response

{
  (...) // standard response part
  "response": "loremipsum324"
}

Yes/No

{
  (...) // standard request part
  "genre": "yes_or_not", // request type
  "required": true, // is it required
  "confirm": true, // is it necessary to confirm the selection in the interface?
  "default": true // not mandatory
  "positive": "Yup", // positive value
  "negative": "Nope", // negative value
}

# Response

{
  (...) // standard response part
  "response": true // or false
}

Radio buttons

{
  (...) // standard request part
  "genre": "combo_boxes", // request type
  "required": true, // is it required
  "confirm": true, // is it necessary to confirm the selection in the interface?
  "default": 1, // not mandatory
  // possible choices
  "choices": {
    "foo": 1,
    "bar": true,
    "jA si o 🇹🇩": "pl"
  }
}

# Response

{
  (...) // standard response part
  "response": 1 // either "pl" or true
}

Checkboxes

{
  (...) // standard request part
  "genre": "checkboxes", // request type
  "required": true, // is it required
  "confirm": true, // is it necessary to confirm the selection in the interface?
  "default": 1, // not mandatory
  // possible choices
  "choices": {
    "foo": 1,
    "bar": true,
    "jA si o 🇹🇩": "pl"
  }
}

# Response

{
  (...) // standard response part
  "response": [1, true, "pl"] // all answers have been selected
}

List

{
  (...) // standard request part
  "genre": "select", // request type
  "required": true, // is it required
  "confirm": true, // is it necessary to confirm the selection in the interface?
  "default": 1, // not mandatory
  "multi_select": true // can you select more than 1 choice?
  // possible choices
  "choices": {
    "foo": 1,
    "bar": true,
    "jA si o 🇹🇩": "pl"
  }
}

# Response

{
  (...) // standard response part
  "response": [1, true, "pl"] // all answers have been selected
}

To get to the parameters sent in the webhook you need to get yourself the data of the whole job, which you can get to by the environment variable (ANYROBOT_PAYLOAD_FILE) that points the path to the payload file. Feel free to check the documentation related to to learn more about this topic.

File, of any type. The body field is encoded according to , and has a format like this:

runner environment variables
RFC2397