Portal API
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
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 runner environment variables to learn more about this topic.
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
seriesbody - 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
File, of any type. The body field is encoded according to RFC2397, and has a format like this:
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
orfailure
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": "[email protected]",
"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
}

Last updated