Workflows
Workflows enable users to build their own automations inside their workspaces.
"Workflows" consist of "Nodes," which can be seen as serverless functions that can be connected with each other.
TABLE_OF_CONTENTS
Trigger
Manual (click button)
This is the default option. It means that the workflow only triggers when you manually click on "execute" in the workflow UI.
Cron (AWS cron)
Cron allows you to time your workflow execution very precisely (to the minute). We use the AWS Cron format to ensure a reliable scheduling experience.
Webhook (URL + payload)
OrbitType generates a webhook URL, which serves as your entry point. If a third-party app or your bot from a different client sends a POST request with a JSON body to this endpoint, it will serve as the input JSON for the first node. This input will also be saved in your workspace as an example to simplify testing. You can load it with "Load Input" in the first node’s code editor.
Database (table events and OrbitType API triggers)
As OrbitType is an integrated environment, you can listen for triggers from database tables. We support listening to creation, updates, and deletion events. The SQL request triggering the workflow must be executed using the OrbitType API and include a defined return value. For testing, the input is saved so it can be retrieved later.
If a row is updated, you will receive an array containing two objects as input—one with the old row and one with the new row. You can then use JavaScript within the node to precisely evaluate whether the changes should trigger the flow or whether to return early.
Versions/restore
Each workflow is version-controlled; with every save of a node, the complete workflow is saved. You can revert these versions using the Versions button, which then restores the version. It keeps 50 versions as backups.
Node types
Code Nodes
This is the default node. Inside this node, you can write code and set config values to use within the code.
Decorative Nodes
These are visual elements which help you document and visually organize the workflow plan. They can also provide useful context to Orbitype Intelligence.
Node settings
Entry Node
Each workflow has one specific entry node. It is the first one placed on the node canvas by convention or the one you set manually in the node settings.
Hard Limit
Nodes can run for up to 60 minutes. As you can chain nodes together, this can result in very long-running tasks. To prevent nodes from running too long in situations where they clearly should not exceed a few minutes, you can set a hard limit to cut off the runtime. This is not a graceful process, meaning you will receive no output if the node times out.
This can be very useful when experimenting with AI agents and prompts that are not optimized, which may lead to long-running tasks and wasted credits if no hard limit is set.
Naming
Proper naming of nodes helps you, your coworkers, and Orbitpye Intelligence better understand the purpose of each node. It is important to clearly name each node based on its function.
Node function structure + logging
Orbitype Nodes must use the following syntax, with only one function allowed. Inside this function, you can use normal Node.js. Please keep in mind that this is a serverless environment, so some OS-related functionality will not work.
In Orbitype functions, you never need to require or import anything. Instead, you can provide the allowed libraries and API products via dependency injection into the function and then use them.
async ({input}) => {
return {output: input}
}
If you want to log information, it is recommended to create a debug array and push messages to it. At the end, you can return it alongside other things. This way, the next node can analyze errors or you can process them further.
Node Output / Next node param
Output einer Node
If you just return something in the function, this will be set as the output.
If you want to have more control and also use features like next, you need to return an object with the key output. Then you can put it in there, and it will become clean, not boxed input for the next node.
async () => {
const output = Math.random()
return {output,
next: output > 0.5 ? "idOfTheNextNode" : "idOfTheOtherNextNode"}
}Next Attribute
The next attribute allows you to set the next node dynamically. This works only when two or more nodes are connected with the output handle of the node.
async () => {
const output = Math.random()
return {output, next: output > 0.5 ? "idOfTheNextNode" : "idOfTheOtherNextNode"}
}Flow types
Linear
This is an easy workflow, moving from a -> b -> c. It's the easiest and most common way.
Branching
If you have a workflow path which, at some point, needs to decide how to proceed with path a.a or path a.b, you can set the next parameter to test this.
Multi Input
Every workflow has only one entry node, but you can also add another node and connect it to the second node if this helps with testing an input payload. Later in the flow, it can also happen that you have multiple inputs. Keep in mind that the payload can vary in the input, as two or more different nodes may fill it. If you don't work with the same format in the previous nodes, then it can lead to errors in the node with the multiple inputs.
Looping
This is great to have conditions within a workflow, but keep in mind, wrong conditions can lead to endless loops, which can again burn tokens. Use looping only in very controlled settings with clear exit rules.
Sub Workflow Architecture
If a workflow consists of clearly separable sub-workflows, it mostly makes sense to build them into their own workflows and trigger them via webhooks. This way, you get a far more modular workspace structure and can use and test sub-parts in isolation.
Orbitype Enviroment Insertions
Orbi Object
This is an object you can import using dependency injection. It includes a lot of information about the current workspace, allowing you to access it programmatically.
async ({ orbi, linkedin }) => {
// start
const id = orbi.project.credentials.find((x) => x.name === "linkedin").value
const self = await linkedin.connect(id)
}Credentials
This is a more direct way to access credentials. However, it may be deprecated in favor of the Orbi object.
async ({ credentials }) => {
//
let apiKey = credentials.myApiKey
}Agent
orbitype workflow functions have access to an injected agent object. it is a preconfigured wrapper around langchain with two methods: addTool() registers a tool run() runs the agent and returns the final message
example:
const func = async ({agent, zod}) => {
const { z } = zod
agent.addTool(
(id) => {
const url = `https://jsonplaceholder.typicode.com/todos/${id}`
return fetch(url).then((x) => x.json())
},
{
name: "fetch_todo",
description: "Fetches a todo by ID from JSONPlaceholder",
schema: z.string()
},
)
agent.addTool(
(text) => text.trim().split(/\s+/).length,
{
name: "count_words",
description: "Counts the number of words in a given string",
schema: z.string(),
returnDirect: true,
},
)
const data = await agent.run("number of words in todo with id 3")
return data
}Browser
orbitype workflow functions have access to an injected browser object. its run method executes puppeteer code remotely on a different server. the function passed as the first parameter is stringified and sent via http. the second parameter is json serialized and allows injecting variables from outside. finally, the result is returned.
example:
async ({browser}) => {
const url = "https://vuejs.org/guide/introduction.html"
const clicks = 3
const html = await browser.run(
// puppeteer code
async ({page, data}) => {
await page.goto(data.url)
const button = page.locator(".demo > button")
for (let i = 0; i < data.clicks; i++)
await button.click()
return button.map((x) => x.textContent).wait()
},
// injected variables
{url, clicks}
)
return html
}AI
Use AI Features easely in your nodes:
async ({ai}) => {
const text = await ai.chat("This is a test")
// -> "this is an answer..."
const json = await ai.chat("JSON for red rgb color", true)
// -> {r: 255, g: 0, b: 0}
return {text , json}
}
async ({ai}) => {
return await ai.image("a red apple")
// -> { base64: "iVBORw..." }
}
async ({ai}) => {
return await ai.embedding("lorem ipsum")
// -> [-0.0014445713,-0.021729672, ... ]
}OCR
orbitype workflow functions have access to an injected ocr object. fileToText() returns the plain text of a file. fileToTable() returns the tabular data extracted from file. fileToForm() returns fields from an PDF Formular
examples:
async ({ocr}) => {
const url = "https://cdn.orbitype.com/sXbDwXihqsp0/pdfs/sample.pdf"
const resp = await fetch(url)
const blob = await resp.blob()
const file = new File([blob], "dummmy.pdf", {type: "application/pdf"})
const text = ocr.fileToText(file)
return text
}
async ({ocr}) => {
const url = "https://cdn.orbitype.com/sXbDwXihqsp0/pdfs/table.png"
const resp = await fetch(url)
const blob = await resp.blob()
const file = new File([blob], "table.png", {type: "image/png"})
const table = ocr.fileToTable(file)
return table
}
async ({ocr}) => {
const url = "https://cdn.orbitype.com/sXbDwXihqsp0/pdfs/table.png"
const resp = await fetch(url)
const blob = await resp.blob()
const file = new File([blob], "table.png", {type: "image/png"})
const table = ocr.fileToForm(file)
return table
}
// tabular data is returned as a 2d array, e.g.
[
["name", "hex", "temperature"],
["red", "#FF0000", "warm"],
["blue", "#0000FF", "cold"],
["yellow", "#FFFF00", "warm"],
["cyan", "#00FFFF", "cold"]
]LinkedIn (advanced)
LinkedIn Integration in Orbitype
This documentation describes all available LinkedIn actions in Orbitype.
It explains what each action does, which parameters are required, and provides one complete example that uses every LinkedIn action in a logical flow.
All LinkedIn functionality is available via the linkedin object inside the Orbitype code environment.
Prerequisites
A credential with the name
linkedinexists in the projectThe code runs inside an Orbitype function or agent
The LinkedIn account must be connected before any other action
Basic Usage Pattern
Every LinkedIn workflow follows this order:
Connect the LinkedIn account
Optionally fetch profiles or search results
Perform actions like invitations, messages, or posts
Minimal setup:
async ({ orbi, linkedin }) => {
const accountId = orbi.project.credentials.find(x => x.name === "linkedin").value
await linkedin.connect(accountId)
}
Without connect(), no other method will work.
Connection
linkedin.connect(accountId)
Description
Connects the LinkedIn account and initializes the session.
This method must always be called first.
Parameters
accountId(string, required)
Value fromorbi.project.credentialsfor the credential namedlinkedin
Returns
{
account_id: string,
provider_id: string
}
provider_id represents your own LinkedIn profile and is required for posts and profile based queries.
Profiles and Search
linkedin.getProfile(identifier)
Description
Loads a LinkedIn profile using a profile identifier.
Parameters
identifier(string, required)
LinkedIn profile handle from the URL, for examplejulian-vorraro
Returns (relevant fields)
{
first_name: string,
last_name: string,
provider_id: string
}
linkedin.search(params)
Description
Searches for LinkedIn profiles using flexible search criteria.
Parameters
params(object, required)
Search criteria, for example:keywords(string)
Returns
Array<object>list of search result items
Invitations
linkedin.sendInvitation(providerId, message)
Description
Sends a connection request to a LinkedIn profile.
Parameters
providerId(string, required)
Profile ID fromgetProfile()orsearch()message(string, required)
Invitation message text
linkedin.getInvitations()
Description
Returns all sent connection requests.
Parameters
none
Returns
objectcontaining invitation items
linkedin.cancelInvitation(invitationId)
Description
Cancels a previously sent invitation.
Parameters
invitationId(string, required)
Invitation ID fromgetInvitations()
Chats and Messages
linkedin.startChat(text, attendeesIds)
Description
Starts a new chat and sets it as the active chat.
Parameters
text(string, required)
Initial messageattendeesIds(string[], optional)
List of profileproviderIds
linkedin.resumeChat(chatId)
Description
Sets an existing chat as the active chat.
Parameters
chatId(string, required)
linkedin.getChats()
Description
Lists existing chats.
Parameters
none
linkedin.getMessages()
Description
Fetches messages from the currently active chat.
Requirement
An active chat must be set
linkedin.sendMessage(text)
Description
Sends a message in the active chat.
Parameters
text(string, required)
Posts and Comments
linkedin.createPost(text)
Description
Creates a new LinkedIn post.
Parameters
text(string, required)
linkedin.getPosts(identifier)
Description
Fetches posts from a LinkedIn profile.
Parameters
identifier(string, required)
Usually your ownprovider_idfromconnect()
linkedin.sendComment(postId, text)
Description
Adds a comment to a LinkedIn post.
Parameters
postId(string, required)
Post ID fromgetPosts()text(string, required)
Complete Example Using All LinkedIn Actions
This example uses every available LinkedIn action exactly once in a realistic workflow.
async ({ orbi, linkedin }) => {
// 1) Connect LinkedIn account
const accountId = orbi.project.credentials.find(x => x.name === "linkedin").value
const self = await linkedin.connect(accountId)
// 2) Search for people
const searchResults = await linkedin.search({
keywords: "Webentertainer GmbH"
})
// 3) Load a profile
const profile = await linkedin.getProfile("julian-vorraro")
// 4) Send invitation
await linkedin.sendInvitation(
profile.provider_id,
"Hi Julian, quick connection?"
)
// 5) Fetch sent invitations
const invitations = await linkedin.getInvitations()
// 6) Cancel an invitation if needed
if (invitations.items?.length) {
await linkedin.cancelInvitation(invitations.items[0].invitation_id)
}
// 7) Start a chat
await linkedin.startChat(
"Thanks for connecting 👋",
[profile.provider_id]
)
// 8) Send a message
await linkedin.sendMessage("Quick exchange about automation?")
// 9) Fetch chats
const chats = await linkedin.getChats()
// 10) Resume an existing chat
if (chats.items?.length) {
await linkedin.resumeChat(chats.items[0].id)
}
// 11) Fetch messages
const messages = await linkedin.getMessages()
console.log(messages.items?.map(m => m.text))
// 12) Create a post
await linkedin.createPost("Automation directly from Orbitype 🚀")
// 13) Fetch own posts
const posts = await linkedin.getPosts(self.provider_id)
// 14) Comment on a post
if (posts.items?.length) {
await linkedin.sendComment(
posts.items[0].id,
"Thanks for reading"
)
}
}
Libraries
orbitype workflow functions have access to a few injected npm libraries.
cheerio
imapflow
lodash
nodemailer
pdf-lib (available as PDFLib)
pdfkit (available as PDFDocument)
unpdf
swissqrbill
zod
manually importing/requiring additional libraries is NOT SUPPORTED
examples:
const func = async ({lodash}) => {
await new Promise((res) => setTimeout(res, 100))
const output = lodash.random(0,9)
return {output}
}
const func = async ({config, input, nodemailer}) => {
const transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
auth: {
user: config.user,
pass: config.pass,
},
})
const mail = {
from: config.user,
to: input.email,
subject: config.subject,
text: config.text,
}
await transporter.sendMail(mail)
return mail
}
const func = async ({ImapFlow}) => {
const client = new ImapFlow({
host: 'smtp.ethereal.email',
port: 993,
secure: true,
auth: {
user: "nathanial.oreilly@ethereal.email",
pass: "BnK2wAJtg4P1Efu5zp",
}
})
await client.connect()
await client.mailboxOpen('INBOX')
const message = await client.fetchOne('*', { source: true })
await client.logout()
return message.source.toString()
}
const func = async ({unpdf}) => {
const url = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'
const res = await fetch(url)
const buffer = await res.arrayBuffer()
const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer))
const { text } = await unpdf.extractText(pdf, { mergePages: true })
return text
}
const func = async ({PDFLib}) => {
const pdfDoc = await PDFLib.PDFDocument.create()
const page = pdfDoc.addPage()
page.drawText('lorem ipsum')
const base64 = await pdfDoc.saveAsBase64({ dataUri: true })
return base64
}
const func = async ({SwissQRBill, PDFDocument}) => {
return new Promise((resolve, reject) => {
const doc = new PDFDocument({ size: 'A4' });
const chunks = [];
doc.on('data', (chunk) => chunks.push(chunk));
doc.on('end', () => resolve(Buffer.concat(chunks).toString('base64')));
doc.on('error', reject);
doc.fontSize(20).text('Invoice #12345', 50, 50);
doc.fontSize(12).text('Lorem ipsum...', 50, 100);
const data = {
amount: 1994.75,
creditor: {
account: "CH44 3199 9123 0008 8901 2",
address: "Musterstrasse",
buildingNumber: 7,
city: "Musterstadt",
country: "CH",
name: "SwissQRBill",
zip: 1234
},
currency: "CHF",
debtor: {
address: "Musterstrasse",
buildingNumber: 1,
city: "Musterstadt",
country: "CH",
name: "Peter Muster",
zip: 1234
},
reference: "21 00000 00003 13947 14300 09017"
};
const qrBill = new SwissQRBill(data);
qrBill.attachTo(doc);
doc.end();
});
}SheetJS (XLSX): Spreadsheet processing
XLSX is SheetJS, a powerful spreadsheet processing library available in Orbitype workflows via dependency injection. It can read, create, edit, and export spreadsheets and tabular data across many formats, making it useful for imports, exports, reporting, and data transformation.
What SheetJS can do
Read spreadsheets and tabular data (for example: XLSX, XLS, CSV and more) from strings, ArrayBuffers, and other supported inputs.
Create and modify workbooks and worksheets programmatically.
Convert between formats (for example: CSV to XLSX, XLSX to CSV).
Transform data structures: sheet to JSON, JSON to sheet, arrays to sheet, and more.
Write/export files (for example: generate XLSX/CSV output for downloads, emails, or storage).
Work with cells: set values, number formats, dates, formulas, and basic worksheet metadata.
Utility helpers for handling ranges, headers, column mappings, and common spreadsheet operations.
example:
async ({ XLSX }) => {
const csv =
`name, age, city, email
John Doe, 30, New York, john@example.com
Jane Smith, 25, Los Angeles, jane@example.com
Bob Johnson, 35, Chicago, bob@example.com`;
const workbook = XLSX.read(csv, { type: "string" });
const sheet = workbook.Sheets['Sheet1'];
const json = XLSX.utils.sheet_to_json(sheet);
return json;
};Notes:
The CSV header row becomes the object keys in the resulting JSON array.
XLSX.readparses the input into a workbook structure.XLSX.utils.sheet_to_jsonconverts worksheet rows into JSON objects.
Learn more
Cancel Account
To ensure you’re comfortable using Orbitype, we want to make sure everything is clear. If you have any questions or encounter issues understanding how something works, please don’t hesitate to reach out. We’re here to assist you—just send us a message at support@orbitype.com, and we’ll be happy to help you with any questions you have.
