Skip to main content

Client Overview

The workflows-client library provides an easy way to interact with the Workflows API.

Initialization

To use the client, you need to create an instance of WorkflowsClient.

import { WorkflowsClient } from '@picsart/workflows-client';

const workflows = new WorkflowsClient({
apiKey: 'Bearer <YOUR_API_KEY>'
})

Client Options

OptionTypeDescription
apiKeystringUser access token. Sent as Authorization: Bearer <apiKey>
identityTokenstringClient/app token. Sent as x-app-authorization: Bearer <identityToken>.
baseUrlstringAPI base URL. Defaults to https://api.picsart.com/.
fetchtypeof fetchCustom fetch implementation. Useful in environments like the Miniapp SDK where requests must go through a sandboxed transport.
getRemoteSettings(name, tag?) => Promise<ApiSettings>Resolver for remote settings (e.g. executionMode, configId) — see Settings Integration.
headersHeadersInitDefault headers applied to every request.
socketSocketLikeAn existing socket.io socket for ExecutionMode.SOCKET — you own its lifecycle. See Socket Events.
socketConnectionSocketConnectionOptionsConnection config to let the client create + own a socket (needs socket.io-client). See Socket Events.
note

If neither apiKey nor identityToken is provided and no custom fetch is configured, requests will throw apiKey is not provided. When you pass a custom fetch, auth headers can be injected there instead.

Example in Miniapp SDK context

import { WorkflowsClient } from '@picsart/workflows-client';

const workflows = new WorkflowsClient({
baseUrl: apiprocess.env.REACT_APP_API_URL,
fetch: getContext().utils.api.fetch,
getRemoteSettings: getContext().handlers.getRemoteSettings
})

Basic Usage

The below example will execute the example-task workflow and return its result

const params = {}; // request body
const { result, usage } = await workflows.run<MyResponseType>('example-task', params);

run() resolves to { result, usage } where result is the workflow's typed response and usage carries credit-charging details for the call when credits are configured: the tool that was charged, the credits per single use of the tool, the number of uses charged, the total credits, and an optional per-sub-task breakdown.

Type-safe execution

warning

runTypeSafe() is experimental — its API and the shape of generated types may change.

runTypeSafe() is a variant of run() that infers params and result types from the workflow name. It relies on @picsart/workflows-types, a peer dependency of @picsart/workflows-client. Update it periodically to stay in sync with the latest generated types.

// params and result are inferred from the "example-task" workflow name
const { result } = await workflows.runTypeSafe('example-task', {
name: 'Joe',
});

Existing run() calls keep working — opt in by switching to runTypeSafe().

info

Types are currently generated for a subset of workflows only. Workflows without generated types are not callable via runTypeSafe() — use run() for those.

Execution Modes

The service supports three execution modes:

Asynchronous Mode (Default)

Async is the default mode, where the task executed asynchronously with polling to check for completion.

const result = await workflows.run<MyResponseType>(
'example-task',
params,
{
mode: ExecutionMode.ASYNC, // Optional: Execution mode
pollingInterval: 300, // Optional: Custom polling interval in ms
retriesCount: 150, // Optional: Number of max polling attempts
onAccepted: (taskId) => console.log('Task accepted:', taskId), // optional
onPartialResult: (response) => console.log('Progress:', response) // optional
}
);

Synchronous Mode

Use sync mode if the task is expected to complete quickly (typically under 5 seconds).

const result = await workflows.run<MyResponseType>(
'example-task',
params,
{mode: ExecutionMode.SYNC}
);

Stream Mode

Stream mode maintains a persistent connection and provides intermediate results in real-time. This mode is ideal when updates need to be delivered with minimal delay

import { WorkflowEvent } from '@picsart/workflows-client';

const result = await workflows.run<MyResponseType>(
'example-task',
params,
{
mode: ExecutionMode.STREAM,
onEvent: (event: WorkflowEvent) => console.log('Event received: ', event)
},
);

Here onEvent is triggered whenever the task fires custom event (it is not triggered by framework-native events) Consequently, the redundant event. prefix is omitted from the event.type

Please note that the return value might differ from the one returned in sync/async modes as due to specifics of streaming implementations, some tasks don't compute the final aggregated result and return empty result instead.

Socket Mode

Socket mode runs the task asynchronously but delivers its events pushed over the socket gateway (low latency, no polling) instead of polled. It requires a socket or socketConnection on the client. run() resolves with the result just like async.

import { ExecutionMode, WorkflowEvent } from '@picsart/workflows-client';

const { result } = await workflows.run<MyResponseType>('example-task', params, {
mode: ExecutionMode.SOCKET,
onProgress: (p) => console.log('progress:', p),
onEvent: (e: WorkflowEvent) => console.log('event:', e),
});

See Socket Events for setup (the socket + tokens), watching an existing task with workflows.subscribe(...), closing the socket with workflows.disconnect(), and reconnection behavior.

Execution Options

Every option accepted as the third argument of run():

OptionTypeDescription
modeExecutionModeASYNC (default), SYNC, STREAM, or SOCKET (async submit + pushed events — see Socket Events). Any of these may be overridden by remote settings — see Settings Integration.
remoteSettingNamestringOverride the default remote-settings lookup name. Defaults to ${taskName}_api.
pollingIntervalnumberASYNC only. Delay between polls in ms. Defaults to 300.
retriesCountnumberASYNC only. Maximum number of poll attempts before timing out. Defaults to 1000.
onAccepted(taskId) => voidASYNC/SOCKET. Fires after the task is submitted, with the assigned task id.
onProgress(progress) => voidReceives { percent, estimatedSecondsLeft? } progress updates.
onPartialResult(partial) => voidReceives the most recent partial result emitted by the workflow.
onEvent(event) => voidReceives custom events emitted via flow.emitEvent(). Required for STREAM.
notificationConfigobjectASYNC/SOCKET. Forwarded as the request body's notification field — see Async API.
headersHeadersInitPer-call headers, merged on top of the client-level headers.
abortSignalAbortSignalCancels the in-flight call when aborted.

Cancellation

Pass an AbortSignal via abortSignal to cancel an in-flight call:

const controller = new AbortController();

const promise = workflows.run<MyResponseType>('example-task', params, {
abortSignal: controller.signal,
});

// later
controller.abort();

Aborting rejects the run() promise with a DOMException named AbortError. For ASYNC the polling loop stops; for SOCKET the run stops listening and leaves the channel; for SYNC and STREAM the underlying fetch is cancelled.

Resuming a Submitted Task

If you already have a task id from a previous submission (e.g. saved before a page reload), you can poll for its result without re-submitting:

const { result, usage } = await workflows.runPolling<MyResponseType>(
'example-task',
taskId,
{ pollingInterval: 500 },
);

runPolling accepts the same pollingInterval, retriesCount, onProgress, onPartialResult, onEvent, abortSignal, and headers options as run().

Execution History

executionsHistory(taskName, offset?, limit?, isGrouped?) fetches past executions of a workflow for the current user:

const history = await workflows.executionsHistory<MyResponseType>(
'example-task',
0, // offset, defaults to 0
10, // limit, defaults to 10
false // isGrouped, defaults to false
);

Each entry includes the task id, created timestamp, status, submitted params, and an array of result values (the array shape supports grouped responses).

Error Handling

run() and runPolling() reject with one of three error classes, all exported from @picsart/workflows-client:

ErrorWhen
WorkflowsClientErrorThe server returned a 4xx response, or the workflow itself ended in a FAILED status with a 4xx status code. The instance exposes statusCode, reason, and message.
WorkflowsServerErrorThe server returned a 5xx response, or the workflow ended with a 5xx status code.
WorkflowsUnknownErrorAn unexpected error occurred (e.g. network failure, non-JSON response, or a thrown value that wasn't one of the above).

In ASYNC mode, exceeding retriesCount rejects with WorkflowsClientError (statusCode: 408, reason: 'client_timeout').

import { WorkflowsClientError, WorkflowsServerError } from '@picsart/workflows-client';

try {
const { result } = await workflows.run<MyResponseType>('example-task', params);
} catch (err) {
if (err instanceof WorkflowsClientError) {
// 4xx — bad request, validation, polling timeout, etc.
} else if (err instanceof WorkflowsServerError) {
// 5xx — retry-friendly server failure
}
throw err;
}

Settings Integration

The run method supports an optional executionOptions.remoteSettingName parameter, which allows you to specify the name of the Remote Setting where your workflow configuration is defined.

If not provided, the default setting name will be: ${taskName}_api

By default, the execution options defined in the Remote Setting will override the options explicitly passed to the run method.