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.

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 excludeIds to 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

  1. Start with narrow filters and low perPage (10-25).
  2. Validate relevance from returned data.
  3. Increase perPage only after precision is good.
  4. Use enrich only for the subset you truly need.
  5. Persist processed IDs and always pass them via excludeIds on retries.
  6. 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 apollo exists in the project

  • The 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:

  1. Connect the Apollo account

  2. Search people (raw or normalized)

  3. 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 example q_keywords, page, per_page)

Returns

  • object raw Apollo response (typically includes total_entries and people)


apollo.searchLeads(params)

Description
Normalized search result with stable lead fields.

Parameters

  • params (object, optional)
    Same query shape as search()

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 example id, linkedin_url, reveal_personal_emails)

Returns

  • object raw enrichment response (typically includes person and request_id)


apollo.enrichById(personId, options)

Description
Enriches one person by Apollo person ID.

Parameters

  • personId (string, required)
    Apollo person ID

  • options (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 URL

  • options (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 query

    • page (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 use
  • agent.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 linkedin exists in the project

  • The 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:

  1. Connect the LinkedIn account

  2. Fetch data (profiles, search results, chats, posts) if needed

  3. 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 from orbi.project.credentials for credential name linkedin

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 example julian-vorraro

  • options (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, default 10)
    Maximum number of results to return

Default behavior

  • If not provided in params, search defaults to category = 'people' and api = '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 from getProfile() or search()

  • message (string, required)
    Invitation message text


linkedin.getInvitations()

Description
Returns sent connection requests.

Parameters

  • none

Returns

  • object containing invitation items


linkedin.cancelInvitation(invitationId)

Description
Cancels a previously sent invitation.

Parameters

  • invitationId (string, required)
    Invitation id from getInvitations()


Chats and Messages

linkedin.startChat(text, attendeesIds?)

Description
Starts a new chat and sets it as active chat.

Parameters

  • text (string, required)
    Initial message

  • attendeesIds (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 (startChat or resumeChat)


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 full data:<mime>;base64,...), optional filename (recommended)

Supported extensions

  • Images: jpg, jpeg, png, gif, webp

  • Videos: mp4, mov, webm

  • Documents: 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 own provider_id from connect()


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 from getPostingIdentities()

  • text (string, required)

  • attachments (optional)
    Same attachment formats as createPost()

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, default true)

    • includeReactions (boolean, optional, default true)

    • 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, default 20)

    • 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 startChat or resumeChat.

  • 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.read parses the input into a workbook structure.

  • XLSX.utils.sheet_to_json converts 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.

Build your GTM edge before your competitors do.

|
page-logo

Orbitype learns your business, analyzes competitors, finds prospects, and autonomously builds and operates agentic growth systems tailored to your company.

Get started for free

2026 Orbitype. All rights reserved.