Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iii-hq/sdk/llms.txt

Use this file to discover all available pages before exploring further.

III provides multiple ways to invoke functions, both synchronously (awaiting results) and asynchronously (fire-and-forget).

call()

Invoke a function and wait for the result.
const result = await iii.call<TInput, TOutput>(function_id, data, timeoutMs?)
function_id
string
required
ID of the function to invoke (e.g., 'users::create')
data
TInput
required
Input data to pass to the function
timeoutMs
number
Optional timeout in milliseconds. Defaults to invocationTimeoutMs from init() options (120000ms / 2 minutes)
result
TOutput
The function’s return value

Example: Basic Call

import { init } from 'iii-sdk'

const iii = init('ws://localhost:49199')

// Register a function
iii.registerFunction(
  { id: 'math::add' },
  async (data: { a: number; b: number }) => {
    return { sum: data.a + data.b }
  }
)

// Call the function
const result = await iii.call<
  { a: number; b: number },
  { sum: number }
>('math::add', { a: 5, b: 3 })

console.log(result.sum) // 8

Example: With Timeout

try {
  // Set a 5-second timeout
  const result = await iii.call(
    'slow::function',
    { data: 'input' },
    5000
  )
  console.log('Result:', result)
} catch (error) {
  console.error('Invocation failed:', error.message)
  // "Invocation timeout after 5000ms: slow::function"
}

Example: Type-Safe Calls

interface CreateUserInput {
  email: string
  name: string
}

interface CreateUserOutput {
  id: string
  email: string
  name: string
  created_at: string
}

const user = await iii.call<CreateUserInput, CreateUserOutput>(
  'users::create',
  {
    email: 'john@example.com',
    name: 'John Doe'
  }
)

// TypeScript knows the shape of `user`
console.log(user.id)
console.log(user.email)

callVoid()

Invoke a function asynchronously without waiting for a response (fire-and-forget).
iii.callVoid<TInput>(function_id, data)
function_id
string
required
ID of the function to invoke
data
TInput
required
Input data to pass to the function
callVoid() returns immediately and does not wait for the function to complete. Use this for background tasks, logging, notifications, etc.

Example: Fire-and-Forget

// Send analytics event without waiting
iii.callVoid('analytics::track', {
  event: 'user_login',
  user_id: '123',
  timestamp: Date.now()
})

// Send notification asynchronously
iii.callVoid('notifications::send_email', {
  to: 'user@example.com',
  subject: 'Welcome!',
  body: 'Thanks for signing up'
})

console.log('Events sent!')

Example: Background Processing

iii.registerFunction(
  { id: 'orders::create' },
  async (data: { items: string[]; user_id: string }) => {
    const order = {
      id: crypto.randomUUID(),
      items: data.items,
      user_id: data.user_id,
      created_at: new Date().toISOString()
    }
    
    // Save order synchronously
    await db.orders.create({ data: order })
    
    // Process fulfillment asynchronously
    iii.callVoid('fulfillment::process', { order_id: order.id })
    
    // Send confirmation email asynchronously
    iii.callVoid('email::send_confirmation', {
      email: data.user_id,
      order_id: order.id
    })
    
    return order
  }
)

trigger() / triggerVoid()

Aliases for call() and callVoid() for backward compatibility:
// Same as call()
const result = await iii.trigger('function::id', data, timeout)

// Same as callVoid()
iii.triggerVoid('function::id', data)

Error Handling

Function invocations can fail for various reasons. Always handle errors appropriately:
try {
  const result = await iii.call('users::get', { id: '123' })
  console.log('User:', result)
} catch (error) {
  if (error.message.includes('not found')) {
    console.error('User does not exist')
  } else if (error.message.includes('timeout')) {
    console.error('Request timed out')
  } else {
    console.error('Unexpected error:', error)
  }
}

Common Error Messages

Invocation timeout after 120000ms: function::id
The function didn’t respond within the timeout period. Increase the timeout or optimize the function.
Function not found
No worker has registered the requested function. Check the function ID and ensure the worker is connected.
invocation_failed: [error message]
The function threw an error. Check the error message for details.
iii is shutting down
The SDK is shutting down and all pending invocations are being rejected.

Distributed Tracing

All invocations automatically propagate W3C trace context for distributed tracing:
import { getContext } from 'iii-sdk'

iii.registerFunction(
  { id: 'service_a::process' },
  async (data: { value: string }) => {
    const { logger, trace } = getContext()
    
    // Add custom span attributes
    trace?.setAttribute('input.value', data.value)
    
    logger.info('Processing in service A')
    
    // Call another function - trace context is propagated
    const result = await iii.call('service_b::transform', data)
    
    logger.info('Received result from service B')
    
    return result
  }
)

iii.registerFunction(
  { id: 'service_b::transform' },
  async (data: { value: string }) => {
    const { logger, trace } = getContext()
    
    // This function is part of the same trace
    logger.info('Transforming in service B')
    trace?.setAttribute('transform.type', 'uppercase')
    
    return { transformed: data.value.toUpperCase() }
  }
)
The trace context flows automatically:
  1. Service A starts a span
  2. Service A calls Service B
  3. Service B receives the trace context and creates a child span
  4. Both spans are linked in the distributed trace

Channel Passing

Pass channel references in invocation data for streaming:
// Create a channel
const channel = await iii.createChannel()

// Pass channel reader to another function
const resultPromise = iii.call('processor::analyze', {
  data_source: channel.readerRef
})

// Write data to the channel
channel.writer.stream.write(Buffer.from('chunk 1'))
channel.writer.stream.write(Buffer.from('chunk 2'))
channel.writer.stream.end()

// Wait for processing to complete
const result = await resultPromise
See Channels API for details.

Performance Tips

// Don't block on analytics
iii.callVoid('analytics::track', event)

// Don't wait for cache warming
iii.callVoid('cache::warm', { keys })
// Fast operation - short timeout
await iii.call('cache::get', { key }, 1000)

// Slow operation - longer timeout
await iii.call('ml::predict', { data }, 60000)
// Sequential (slow)
const user = await iii.call('users::get', { id })
const orders = await iii.call('orders::list', { user_id: id })
const preferences = await iii.call('preferences::get', { user_id: id })

// Parallel (fast)
const [user, orders, preferences] = await Promise.all([
  iii.call('users::get', { id }),
  iii.call('orders::list', { user_id: id }),
  iii.call('preferences::get', { user_id: id })
])
const results = await Promise.allSettled([
  iii.call('service1::fn', data),
  iii.call('service2::fn', data),
  iii.call('service3::fn', data)
])

results.forEach((result, i) => {
  if (result.status === 'fulfilled') {
    console.log(`Service ${i + 1}:`, result.value)
  } else {
    console.error(`Service ${i + 1} failed:`, result.reason)
  }
})

Best Practices

// Good - type-safe
const result = await iii.call<Input, Output>('fn::id', data)

// Avoid - no type safety
const result = await iii.call('fn::id', data)
// Good
await iii.call('users::create', data)
await iii.call('orders::cancel', { id })

// Avoid
await iii.call('create', data)
await iii.call('fn_42', { id })
// Default timeout for normal operations
const iii = init(url, { invocationTimeoutMs: 30000 })

// Override for specific calls
await iii.call('fast::operation', data, 5000)
await iii.call('slow::operation', data, 120000)

Build docs developers (and LLMs) love