Service API Reference

Services are supervised capabilities available inside scripts. They provide a stable WOML interface for common external effects while Rust records attempts, timeouts, safe metadata, idempotency identity, cancellation, and bounded results.

Managed failures are WomlServiceError values with code, service, operation, callId, retryable, ambiguous, and bounded details. Catch an error only when the workflow can make a meaningful domain decision; otherwise let the step fail so retries and lifecycle behavior remain honest.

HTTP

services.http.request(request, options?) accepts URL, method, headers, query, one body form, response type, timeout, accepted status, redirect policy, optional storage target, and idempotency header/value. It returns { status, ok, headers, data, url, redirected }.

JavaScript
const response = await services.http.request({
  method: 'POST',
  url: 'https://api.example.com/orders',
  headers: { authorization: `Bearer ${secrets.API_TOKEN}` },
  json: { orderId: context.payload.orderId },
  responseType: 'json',
  timeout: '10s',
  acceptedStatus: { minimum: 200, maximum: 201 }
}, {
  name: 'create-order'
});

return response.data;

Use native fetch() when browser-compatible streaming or lower-level control matters. Managed HTTP is not automatically faster for every request; its main value is a workflow-aware operational contract.

Database

services.db({ driver, connection }) supports query, execute, read, insert, update, delete, and transaction. Drivers are sqlite and postgres. Use parameterized values; update and delete helpers require a non-empty filter.

JavaScript
const db = services.db({
  driver: 'postgres',
  connection: secrets.POSTGRES_URL
});

const customer = await db.read({
  table: 'customers',
  where: { id: context.payload.customerId },
  limit: 1
});

return { customer: customer.rows[0] ?? null };

Use transaction() when several writes must commit or roll back together. Database services manage application records; they are different from the engine's private event store and from services.state.

Storage

services.storage provides put, get, head, list, and delete. Objects use logical keys, content-derived versions, checksums, size, and content type. Local objects are limited to 64 MiB.

JavaScript
const stored = await services.storage.put({
  key: `reports/${context.payload.reportId}.json`,
  value: context.steps.buildReport,
  contentType: 'application/json'
});

return { key: stored.key, version: stored.version };

Storage is appropriate for reports, exports, attachments, and intermediate objects too large or durable to keep in workflow context.

Cache

services.cache provides get, set, delete, has, increment, and setIfAbsent. TTL defaults to 5 minutes and may range from 1 ms through 30 days.

JavaScript
const key = `exchange-rate:${context.payload.currency}`;
const cached = await services.cache.get(key);
let rate;

if (cached.hit) {
  rate = cached.value;
} else {
  rate = await loadRate(context.payload.currency);
  await services.cache.set(key, rate, { ttl: '10m' });
}

return { rate };

Cache is an optimization. The workflow must remain correct after an entry expires or disappears.

State

services.state provides versioned get, has, set, delete, increment, and setIfAbsent. Every mutation requires a stable name; optional ifVersion enables compare-and-set.

JavaScript
const count = await services.state.increment(
  'daily-orders',
  1,
  { name: 'increment-daily-orders' }
);

return { processedToday: count.value };

State is small durable workflow memory shared across runs, such as a cursor, watermark, counter, or last-seen identifier. It is not a replacement for an application database.

Communication

services.events.emit(name, payload, options) broadcasts internally. services.workflows.call(id, payload, options) waits for a result. services.workflows.start(id, payload, options) returns { workflowId, runId, duplicate } after durable admission.

JavaScript
const risk = await services.workflows.call('calculate-risk', {
  customerId: context.payload.customerId
});

await services.workflows.start('send-receipt', {
  orderId: context.payload.orderId
});

await services.events.emit('order.completed', {
  orderId: context.payload.orderId
});

return { risk };

call() waits and returns the child workflow's terminal JSON result. A successful child must produce JSON, including explicit null when that is its contract; missing or undefined output fails the call. start() admits a separate run and lets the parent continue. Events broadcast one fact to all matching active subscribers.

Messaging

services.telegram.send(), services.discord.send(), and services.whatsapp.send() send supervised provider messages. services.queue and services.slack.send() are not public services.

Telegram and Discord support ordinary outbound messages and return provider identity including conversation and message IDs. WhatsApp v1 sends approved templates rather than arbitrary proactive free-form messages.

JavaScript
return await services.telegram.send({
  botToken: secrets.TELEGRAM_BOT_TOKEN,
  conversationId: context.payload.chatId,
  text: 'Your report is ready.'
}, {
  name: 'send-report-ready'
});

For approval and lifecycle notifications, declarative provider tags are usually clearer. Use messaging services when sending is part of ordinary script logic.