Runtime Environment
Orbitype has a shared runtime environment which is accessible to agents through their tools, workflows in their nodes, artifacts in their server actions, and to Orbitype Intelligence in the interactive execution mode.
TABLE_OF_CONTENTS
Orbitype Environment Injections
Orbi Object
Workflow functions have access to an injected orbi object. It represents the current project context and lets you access project metadata, connectors, and configured credentials in a consistent way.
async ({ orbi }) => {
const { id, name } = orbi.project
return { projectId: id, projectName: name }
}Connectors
orbi.project.connectors lists the connectors available in the current project (e.g. SQL, S3). Use this to discover what integrations exist and to reference connector IDs when needed.
async ({ orbi }) => {
const connectors = orbi.project.connectors.map(c => ({
id: c.id,
name: c.name,
implementation: c.implementation
}))
return { connectors }
}Credentials
orbi.project.credentials contains configured credential entries for the current project. This is the recommended way to access credential values.
async ({ orbi, linkedin }) => {
const accountId = orbi.project.credentials
.find(x => x.name === 'linkedin')?.value
if (!accountId) {
throw new Error('Missing credential: linkedin')
}
const self = await linkedin.connect(accountId)
return { providerId: self.provider_id }
}API Keys
orbi.project.apiKeys contains API key metadata for the current project. Treat API keys as secrets: do not log them and do not return them in workflow output in production.
async ({ orbi }) => {
const keys = orbi.project.apiKeys.map(k => ({
name: k.name,
connectorId: k.connectorId
}))
return { apiKeys: keys }
}Leads Database
Use the Leads Database to find matching people and optionally enrich them with contact data. The same behavior is available in assistant tooling and in workflow code.
What it does
- Search mode: returns profile/company/context fields (no direct contact reveal).
- Enrich mode: returns selected contact fields for specific leads.
- Pagination: supports page/perPage and cursor-based pagination.
- Idempotency: supports
excludeIdsto skip already-processed leads. - Credit-aware: costs depend on mode and selected enrich fields.
Input model
Common inputs:
{
mode?: "search" | "enrich", // default: "search"
confirmed?: boolean, // set true after explicit user approval for high-cost enrich
page?: number, // default: 1
perPage?: number, // max: 100
cursor?: string, // keyset pagination cursor
ids?: string[], // usually for enrich mode
excludeIds?: string[], // skip already processed leads
fields?: string[] // enrich fields only
}Typical filtering options (all optional):
{
query?: string | string[],
keywords?: string[],
excludeKeywords?: string[],
industries?: string[],
jobTitles?: string[],
companyNames?: string[],
locations?: string[],
locationCountries?: string[],
skills?: string[],
interests?: string[],
countries?: string[],
minYearsExperience?: number,
maxYearsExperience?: number,
minLinkedinConnections?: number,
hasCompanyWebsite?: boolean,
hasLinkedinUrl?: boolean
}Query behavior
query: "revops"matches rows where any searchable column contains that term.query: ["revops", "berlin"]applies AND across terms, while each term is matched across multiple columns.
Search vs enrich output
Search mode output (example):
{
data: [
{
id, fullName, firstName, lastName,
industry, jobTitle, subRole,
companyName, companyIndustry, companyWebsite, companySize,
location, locationCountry,
yearsExperience, linkedinConnections,
skills, countries, interests
}
],
hasMore, nextPage, cursor, nextCursor,
enrichment: {
count,
fields,
selectedFieldCostCredits,
estimatedCostCredits,
pricing,
filters
}
}Enrich mode output (example):
{
data: [
{
id,
email,
emailAddresses,
mobileNumbers,
phoneNumbers,
linkedinUrl,
facebookUrl,
twitterUrl,
githubUrl
}
],
rows,
fields,
selectedFieldCostCredits,
costCredits,
hasMore,
nextPage,
cursor,
nextCursor
}Workflow wrapper usage
async ({ leads }) => {
const page1 = await leads.search({
mode: "search",
query: ["revops", "outbound"],
locations: ["Berlin"],
perPage: 50,
excludeIds: ["already-processed-id"]
})
return {
rows: page1.data,
nextCursor: page1.nextCursor,
credits: leads.getCredits()
}
}async ({ leads }) => {
const enriched = await leads.search({
mode: "enrich",
confirmed: true,
ids: ["lead-id-1", "lead-id-2"],
fields: ["email", "linkedinUrl"]
})
return {
rows: enriched.data,
cost: enriched.costCredits,
credits: leads.getCredits()
}
}async ({ leads }) => {
const batch = await leads.searchMany(
{
mode: "search",
query: "b2b saas",
excludeIds: ["already-processed-id"]
},
{ maxRows: 150 }
)
return {
count: batch.count,
rows: batch.data,
limited: batch.limited,
cost: batch.costCredits
}
}Best-practice flow
- Start with narrow filters and low
perPage(10-25). - Validate relevance from returned data.
- Increase
perPageonly after precision is good. - Use enrich only for the subset you truly need.
- Persist processed IDs and always pass them via
excludeIdson retries. - If cost confirmation is required, request user approval first, then rerun with
confirmed: true.
Apollo Integration
Apollo Integration in Orbitype
This documentation describes all available Apollo actions in Orbitype.
It explains what each action does, which parameters are required, and provides one complete example that uses every Apollo action in a logical flow.
All Apollo functionality is available via the apollo object inside the Orbitype code environment.
Prerequisites
A credential with the name
apolloexists in the projectThe code runs inside an Orbitype function or agent
The Apollo account must be connected before any other action
Basic Usage Pattern
Every Apollo workflow follows this order:
Connect the Apollo account
Search people (raw or normalized)
Enrich selected leads and track credits
Minimal setup:
async ({ orbi, apollo }) => {
const credential = orbi.project.credentials.find(x => x.name === "apollo")
if (!credential?.value) throw new Error("missing [apollo] credential")
apollo.connect(credential.value)
}
Without connect(), all request methods fail.
Connection
apollo.connect(accessToken)
Description
Connects Apollo with a bearer access token.
This method must always be called first.
Parameters
accessToken(string, required)
Apollo access token from credential value
Returns
{
connected: true
}
Search
apollo.search(params)
Description
Raw people search through Apollo mixed_people API endpoint.
Parameters
params(object, optional)
Raw Apollo query params (for exampleq_keywords,page,per_page)
Returns
objectraw Apollo response (typically includestotal_entriesandpeople)
apollo.searchLeads(params)
Description
Normalized search result with stable lead fields.
Parameters
params(object, optional)
Same query shape assearch()
Returns
{
totalEntries: number,
leads: [
{
id: string,
fullName: string,
title: string,
linkedinUrl: string,
hasEmail: boolean,
companyName: string
}
]
}
Enrichment
apollo.enrichPerson(params)
Description
Raw enrichment call to Apollo people/match endpoint.
Parameters
params(object, optional)
Apollo enrichment params (for exampleid,linkedin_url,reveal_personal_emails)
Returns
objectraw enrichment response (typically includespersonandrequest_id)
apollo.enrichById(personId, options)
Description
Enriches one person by Apollo person ID.
Parameters
personId(string, required)
Apollo person IDoptions(object, optional)
Extra enrichment params merged into the request
Returns
{
person: object | undefined,
request_id: number | undefined
}
apollo.enrichByLinkedinUrl(linkedinUrl, options)
Description
Enriches one person by LinkedIn URL.
Parameters
linkedinUrl(string, required)
Full LinkedIn URLoptions(object, optional)
Extra enrichment params merged into the request
Returns
{
person: object | undefined,
request_id: number | undefined
}
Combined Workflow Helper
apollo.searchAndEnrich(input)
Description
Searches leads by keywords and enriches a subset of results by ID.
Parameters
input(object, required)query(string, required): keyword querypage(number, optional, default 1)perPage(number, optional, default 10)maxEnrich(number, optional, default 3, clamped to 0..25)
Returns
{
search: {
totalEntries: number,
leads: ApolloLead[]
},
enriched: ApolloPerson[],
errors: [
{
personId: string,
message: string
}
]
}
Credits
apollo.getCredits()
Description
Returns credit units consumed by this Apollo integration instance during execution.
Parameters
none
Returns
number
Complete Example Using All Apollo Actions
This example uses every available Apollo action exactly once in a realistic workflow.
async ({ orbi, apollo }) => {
// 1) Connect Apollo account
const credential = orbi.project.credentials.find(x => x.name === "apollo")
if (!credential?.value) throw new Error("missing [apollo] credential")
apollo.connect(credential.value)
// 2) Raw search
const rawSearch = await apollo.search({
q_keywords: "founder saas zurich",
page: 1,
per_page: 5
})
// 3) Normalized search
const normalized = await apollo.searchLeads({
q_keywords: "founder saas zurich",
page: 1,
per_page: 5
})
// 4) Raw enrich (from raw search)
const firstRawPerson = rawSearch?.people?.[0]
let rawEnrichment = null
if (firstRawPerson?.linkedin_url) {
rawEnrichment = await apollo.enrichPerson({
linkedin_url: firstRawPerson.linkedin_url,
reveal_personal_emails: true
})
}
// 5) Enrich by Apollo ID
const firstLead = normalized.leads[0]
let enrichById = null
if (firstLead?.id) {
enrichById = await apollo.enrichById(firstLead.id, {
reveal_personal_emails: true
})
}
// 6) Enrich by LinkedIn URL
let enrichByLinkedin = null
if (firstLead?.linkedinUrl) {
enrichByLinkedin = await apollo.enrichByLinkedinUrl(firstLead.linkedinUrl, {
reveal_personal_emails: true
})
}
// 7) Combined helper
const searchAndEnrich = await apollo.searchAndEnrich({
query: "founder saas zurich",
page: 1,
perPage: 10,
maxEnrich: 2
})
// 8) Credits
const credits = apollo.getCredits()
return {
totals: {
rawTotalEntries: rawSearch?.total_entries ?? 0,
normalizedTotalEntries: normalized.totalEntries
},
firstLead,
rawEnrichment,
enrichById,
enrichByLinkedin,
searchAndEnrich,
credits
}
}
Agency
In orbitype workflow functions, agency is injected automatically. It gives you access to your configured agents by slug.
Use agency.get(slug) to select an agent, then call run(prompt) to execute it.
agency.get(slug)selects the agent you want to useagent.run(prompt)runs that agent and returns the result
Example:
async ({ agency }) => {
const agent = agency.get("immo_research_agent")
if (!agent) {
return { error: "Agent not found: immo_research_agent" }
}
try {
const run = await agent.run("start prompt for my Agent")
return { run }
} catch (e) {
return { error: String(e) }
}
}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
}SERP
orbitype workflow functions have access to an injected serp object. its search method performs a search engine query and returns an array of result URLs (ordered by relevance).
example:
async ({serp}) => {
return {output: await serp.search('orbitype')}
}output:
[
"https://www.orbitype.com/",
"https://uk.finance.yahoo.com/news/orbitype-com-launches-orbitype-intelligence-150000017.html",
"https://www.orbitype.com/de",
"https://app.orbitype.com/guest/register",
"https://www.orbitype.com/posts",
"https://www.globenewswire.com/news-release/2026/01/06/3213801/0/en/Orbitype-com-Launches-Orbitype-Intelligence-a-Chat-Based-Control-Layer-for-AI-Agent-Environments.html",
"https://www.orbitype.com/vs",
"https://www.crunchbase.com/organization/orbitype",
"https://www.orbitype.com/vs/marketing-automation",
"https://webcatalog.io/en/apps/orbitype"
]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, what each action returns, and includes complete usage examples.
All LinkedIn functionality is available via the linkedin object in 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 is connected before any other LinkedIn action
Basic Usage Pattern
Every LinkedIn workflow follows this order:
Connect the LinkedIn account
Fetch data (profiles, search results, chats, posts) if needed
Run actions (invitations, messages, posts, comments, reactions, analytics)
Minimal setup:
async ({ orbi, linkedin }) => {
const accountId = orbi.project.credentials.find(x => x.name === 'linkedin')?.value
if (!accountId) throw new Error('Missing linkedin credential')
await linkedin.connect(accountId)
}
Without connect(), no other method will work.
Connection
linkedin.connect(accountId)
Description
Connects the LinkedIn account and initializes the session.
Parameters
accountId(string, required)
Credential value fromorbi.project.credentialsfor credential namelinkedin
Returns
{
account_id: string,
provider_id: string
}
provider_id identifies your own LinkedIn profile and is used in profile/post operations.
Profiles and Search
linkedin.getProfile(identifier, options?)
Description
Loads a LinkedIn profile by identifier (profile handle).
Parameters
identifier(string, required)
Profile handle from the LinkedIn URL, for examplejulian-vorrarooptions(object, optional)
Optional provider-specific query options
Returns (relevant fields)
{
first_name: string,
last_name: string,
provider_id: string
}
linkedin.search(params, limit?)
Description
Searches LinkedIn using flexible criteria and pagination.
Parameters
params(object, required)
Search criteria, for example:{ keywords: 'Webentertainer GmbH' }limit(number, optional, default10)
Maximum number of results to return
Default behavior
If not provided in
params, search defaults tocategory = 'people'andapi = 'classic'
Returns
Array<object>list of search results
Invitations
linkedin.sendInvitation(providerId, message)
Description
Sends a connection request to a profile.
Parameters
providerId(string, required)
Profile id fromgetProfile()orsearch()message(string, required)
Invitation message text
linkedin.getInvitations()
Description
Returns 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 active chat.
Parameters
text(string, required)
Initial messageattendeesIds(string[], optional)
List of attendee profile ids
Behavior
Your own profile is included automatically
Duplicate attendees are removed automatically
linkedin.resumeChat(chatId)
Description
Sets an existing chat as active chat.
Parameters
chatId(string, required)
linkedin.getChats()
Description
Lists existing chats.
Parameters
none
Behavior
Uses an internal fetch limit of 20 chats per call
linkedin.getMessages()
Description
Fetches messages from the active chat.
Requirement
An active chat must be set (
startChatorresumeChat)
linkedin.sendMessage(text)
Description
Sends a message in the active chat.
Parameters
text(string, required)
Posts and Comments
linkedin.createPost(text, attachments?)
Description
Creates a LinkedIn post. Supports text-only posts, image posts (single or carousel), single-video posts, and single-document posts (PDF, DOC/DOCX, PPT/PPTX).
Parameters
text(string, required)
Post body. Use''for media-only posts.attachments(optional)
One attachment, or an array of attachments. Each attachment can be one of:URL string — for example
'https://.../photo.jpg'{ url, filename? }— URL with optional filename override{ buffer, filename? }— binary buffer, optional filename (recommended){ base64, filename? }— base64 payload (plain base64 or fulldata:<mime>;base64,...), optional filename (recommended)
Supported extensions
Images:
jpg,jpeg,png,gif,webpVideos:
mp4,mov,webmDocuments:
pdf,doc,docx,ppt,pptx
Constraints and notes
Do not mix images and video in the same post.
Document posts should contain exactly one document and no mixed media.
Image carousel: pass an array of image attachments only.
For binary/base64 uploads, always pass a meaningful filename when possible.
Examples
// text only
await linkedin.createPost('hello from orbitype')
// single image by URL
await linkedin.createPost(
'new product launch',
'https://cdn.example.com/launch.jpg'
)
// image carousel
await linkedin.createPost('event recap', [
'https://cdn.example.com/1.jpg',
'https://cdn.example.com/2.jpg',
'https://cdn.example.com/3.jpg'
])
// single video
await linkedin.createPost('short demo', {
url: 'https://cdn.example.com/demo.mp4',
filename: 'demo.mp4'
})
// single PDF
await linkedin.createPost('our latest whitepaper', {
url: 'https://cdn.example.com/whitepaper.pdf',
filename: 'Orbitype Whitepaper 2026.pdf'
})
// PDF from previous step
await linkedin.createPost('monthly report', {
buffer: input.pdfBuffer,
filename: 'report-2026-04.pdf'
})
// image from base64
await linkedin.createPost('ai-generated cover', {
base64: input.coverBase64,
filename: 'cover.png'
})
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)text(string, required)
Reactions
linkedin.reactToPost(postId, reaction?)
Description
Sends a reaction to a post.
Parameters
postId(string, required)reaction(string, optional, default'LIKE')
linkedin.likePost(postId)
Description
Convenience method for reactToPost(postId, 'LIKE').
Parameters
postId(string, required)
Posting Identities
linkedin.getPostingIdentities()
Description
Returns available posting identities for the connected account (member + organization/page identities where available).
Returns
[
{
id: string,
type: 'member' | 'organization' | 'page' | 'unknown',
name: string | null,
provider: 'linkedin',
mailbox_id?: string | null,
raw: unknown
}
]
linkedin.createPostAs(identityId, text, attachments?)
Description
Creates a post as a specific identity id.
Parameters
identityId(string, required)
Identity id fromgetPostingIdentities()text(string, required)attachments(optional)
Same attachment formats ascreatePost()
Returns
Created post payload plus identity metadata (
posting_identity_id,posting_identity_field)
linkedin.createOrganizationPost(organizationId, text, attachments?)
Description
Convenience alias for organization/page posting.
Equivalent
await linkedin.createPostAs(organizationId, text, attachments)
Post Engagement Analytics
linkedin.getPostComments(postId, options?)
Description
Fetches one paginated page of comments for a post.
Parameters
postId(string, required)options(object, optional)cursor(string, optional)limit(number, optional, clamped to 1..100)sort_by(string, optional):'MOST_RECENT'or'MOST_RELEVANT'comment_id(string, optional)
Returns
{
items: any[],
cursor: string | null,
raw: any
}
linkedin.getAllPostComments(postId, options?)
Description
Auto-paginates and returns all comments up to limits.
Parameters
postId(string, required)options(object, optional)limitPerPage(number, optional)maxItems(number, optional)sort_by(string, optional)comment_id(string, optional)
Returns
any[]flattened comments list
linkedin.getPostReactions(postId, options?)
Description
Fetches one paginated page of reactions for a post.
Parameters
postId(string, required)options(object, optional)cursor(string, optional)limit(number, optional, clamped to 1..100)comment_id(string, optional)
Returns
{
items: any[],
cursor: string | null,
raw: any
}
linkedin.getAllPostReactions(postId, options?)
Description
Auto-paginates and returns all reactions up to limits.
Parameters
postId(string, required)options(object, optional)limitPerPage(number, optional)maxItems(number, optional)comment_id(string, optional)
Returns
any[]flattened reactions list
linkedin.getPostEngagedPersons(postId, options?)
Description
Combines comment + reaction actors into a deduplicated engagement view.
Parameters
postId(string, required)options(object, optional)includeComments(boolean, optional, defaulttrue)includeReactions(boolean, optional, defaulttrue)commentSort(string, optional)limitPerPage(number, optional)maxComments(number, optional)maxReactions(number, optional)
Returns
{
people: [
{
id: string | null,
provider_id: string | null,
name: string | null,
headline: string | null,
profile_url: string | null,
sources: Array<'comment' | 'reaction'>,
comments_count: number,
reactions_count: number,
raw_examples: any[]
}
],
comments: any[],
reactions: any[]
}
linkedin.getCompanyActiveEngagers(companyIdentifier, options?)
Description
Fetches company posts and aggregates active engagers across posts.
Parameters
companyIdentifier(string, required)options(object, optional)postsLimit(number, optional, default20)includeComments(boolean, optional)includeReactions(boolean, optional)commentSort(string, optional)limitPerPage(number, optional)maxCommentsPerPost(number, optional)maxReactionsPerPost(number, optional)
Returns
{
company_identifier: string,
posts_scanned: Array<{ id: string, raw: any }>,
engagers: Array<{
id: string | null,
provider_id: string | null,
name: string | null,
headline: string | null,
profile_url: string | null,
posts_count: number,
comments_count: number,
reactions_count: number,
engagement_types: Array<'comment' | 'reaction'>,
post_ids: string[],
comment_texts: string[],
reaction_types: string[],
raw_examples: any[]
}>,
errors: Array<{ scope: 'company' | 'post', message: string, post_id?: string }>
}
Credits
linkedin.getCredits()
Description
Returns rounded consumed credits for the current LinkedIn session in the current execution.
Complete Example
async ({ orbi, linkedin }) => {
// 1) connect
const accountId = orbi.project.credentials.find(x => x.name === 'linkedin')?.value
if (!accountId) throw new Error('Missing linkedin credential')
const self = await linkedin.connect(accountId)
// 2) search + profile
const searchResults = await linkedin.search({ keywords: 'Webentertainer GmbH' }, 10)
const profile = await linkedin.getProfile('julian-vorraro')
// 3) invitations
await linkedin.sendInvitation(profile.provider_id, 'Hi Julian, quick connection?')
const invitations = await linkedin.getInvitations()
if (invitations.items?.length) {
await linkedin.cancelInvitation(invitations.items[0].invitation_id)
}
// 4) chat + messages
await linkedin.startChat('Thanks for connecting 👋', [profile.provider_id])
await linkedin.sendMessage('Quick exchange about automation?')
const chats = await linkedin.getChats()
if (chats.items?.length) await linkedin.resumeChat(chats.items[0].id)
const messages = await linkedin.getMessages()
// 5) create post
const createdPost = await linkedin.createPost('Automation directly from Orbitype 🚀', {
url: 'https://cdn.example.com/post-cover.jpg',
filename: 'cover.jpg'
})
// 6) identities + post as organization/page
const identities = await linkedin.getPostingIdentities()
const orgIdentity = identities.find(x => x.type === 'organization' || x.type === 'page')
if (orgIdentity) {
await linkedin.createPostAs(orgIdentity.id, 'Posted as organization via createPostAs')
await linkedin.createOrganizationPost(orgIdentity.id, 'Posted as organization via alias')
}
// 7) posts list + comment + reaction
const posts = await linkedin.getPosts(self.provider_id)
const firstPostId = posts.items?.[0]?.id || posts.items?.[0]?.post_id || createdPost?.id || createdPost?.post_id
if (firstPostId) {
await linkedin.sendComment(firstPostId, 'Thanks for reading')
await linkedin.reactToPost(firstPostId, 'LIKE')
await linkedin.likePost(firstPostId)
// 8) post engagement analytics
const commentsPage = await linkedin.getPostComments(firstPostId, {
limit: 20,
sort_by: 'MOST_RECENT'
})
const allComments = await linkedin.getAllPostComments(firstPostId, {
maxItems: 100,
sort_by: 'MOST_RECENT'
})
const reactionsPage = await linkedin.getPostReactions(firstPostId, {
limit: 20
})
const allReactions = await linkedin.getAllPostReactions(firstPostId, {
maxItems: 100
})
const engaged = await linkedin.getPostEngagedPersons(firstPostId, {
includeComments: true,
includeReactions: true,
commentSort: 'MOST_RECENT',
maxComments: 200,
maxReactions: 200
})
console.log('comments page:', commentsPage.items?.length)
console.log('all comments:', allComments.length)
console.log('reactions page:', reactionsPage.items?.length)
console.log('all reactions:', allReactions.length)
console.log('engaged persons:', engaged.people?.map(x => x.name))
}
// 9) company-level active engagers
const companyEngagers = await linkedin.getCompanyActiveEngagers('webentertainer', {
postsLimit: 10,
includeComments: true,
includeReactions: true,
commentSort: 'MOST_RECENT',
maxCommentsPerPost: 150,
maxReactionsPerPost: 150
})
// 10) credits
const credits = linkedin.getCredits()
return {
self,
searchResultsCount: searchResults.length,
messagesCount: messages.items?.length ?? 0,
identitiesCount: identities.length,
companyEngagersCount: companyEngagers.engagers.length,
credits
}
}
Troubleshooting
Not connected error: Ensure
linkedin.connect(accountId)runs first.No chat messages: Ensure a chat is active via
startChatorresumeChat.Post upload errors: Check media extension, file size, and filename.
Low analytics data: Engagement may be low, private, or limited by account visibility.
Company engager errors: Use a valid company identifier and ensure account has access to relevant company content.
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.
