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
| Option | Type | Description |
|---|---|---|
apiKey | string | User access token. Sent as Authorization: Bearer <apiKey> |
identityToken | string | Client/app token. Sent as x-app-authorization: Bearer <identityToken>. |
baseUrl | string | API base URL. Defaults to https://api.picsart.com/. |
fetch | typeof fetch | Custom 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. |
headers | HeadersInit | Default headers applied to every request. |
socket | SocketLike | An existing socket.io socket for ExecutionMode.SOCKET — you own its lifecycle. See Socket Events. |
socketConnection | SocketConnectionOptions | Connection config to let the client create + own a socket (needs socket.io-client). See Socket Events. |
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
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().
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():
| Option | Type | Description |
|---|---|---|
mode | ExecutionMode | ASYNC (default), SYNC, STREAM, or SOCKET (async submit + pushed events — see Socket Events). Any of these may be overridden by remote settings — see Settings Integration. |
remoteSettingName | string | Override the default remote-settings lookup name. Defaults to ${taskName}_api. |
pollingInterval | number | ASYNC only. Delay between polls in ms. Defaults to 300. |
retriesCount | number | ASYNC only. Maximum number of poll attempts before timing out. Defaults to 1000. |
onAccepted | (taskId) => void | ASYNC/SOCKET. Fires after the task is submitted, with the assigned task id. |
onProgress | (progress) => void | Receives { percent, estimatedSecondsLeft? } progress updates. |
onPartialResult | (partial) => void | Receives the most recent partial result emitted by the workflow. |
onEvent | (event) => void | Receives custom events emitted via flow.emitEvent(). Required for STREAM. |
notificationConfig | object | ASYNC/SOCKET. Forwarded as the request body's notification field — see Async API. |
headers | HeadersInit | Per-call headers, merged on top of the client-level headers. |
abortSignal | AbortSignal | Cancels 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:
| Error | When |
|---|---|
WorkflowsClientError | The 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. |
WorkflowsServerError | The server returned a 5xx response, or the workflow ended with a 5xx status code. |
WorkflowsUnknownError | An 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.