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. A plain object, a Headers instance, or an array of entries.
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 throw a WorkflowsError (reason: 'invalid_state', message 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, status, usage } = await workflows.run<MyResponseType>('example-task', params);

run() resolves to the task record (WorkflowResponse):

FieldDescription
resultThe workflow's response.
statusThe terminal status the task reached. In ASYNC mode a task that ends FAILED resolves (it doesn't reject) — check status.
usageCredit-charging details when credits are configured: the tool that was charged, the credits per single use, the number of uses charged, the total credits, and an optional per-sub-task breakdown.
id, updated, progress, eventsThe task id, last update time, progress, and emitted events. Filled in ASYNC and SYNC modes; STREAM and SOCKET return only result, usage, and status.

Type-safe execution​

warning

Generated workflow types are experimental — their shape may change.

run() infers params and result types from the workflow name for every workflow that has generated types in @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.run('example-task', {
name: 'Joe',
});

Workflows without generated types take unknown params and return the result type you pass (run<MyResponseType>(...)). Passing the type argument explicitly also turns inference off for a workflow that does have generated types.

Execution Modes​

The service supports four 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. In ASYNC mode they are sent with the submit request, not with the polls.
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 before its next poll; for SOCKET the run stops listening and leaves the channel; for SYNC and STREAM the underlying fetch is cancelled.

Submitting Without Waiting​

submit() starts a task and resolves with its id, without waiting for the result. Only headers, notificationConfig, and remoteSettingName apply:

const taskId = await workflows.submit('example-task', params);

Consume the result later with runPolling(), result(), or subscribe().

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, status, usage } = await workflows.runPolling<MyResponseType>(
'example-task',
taskId,
{ pollingInterval: 500 },
);

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

Polling rides out short connection drops: a poll that doesn't reach the server is retried with backoff, up to 10 times in a row (about 30 seconds at the default interval), before the run fails with reason: 'connection_error'.

Checking a Task Once​

result() fetches the task's current state with a single request — no polling. result may still be empty or partial, so read status:

const { status, progress, result } = await workflows.result<MyResponseType>('example-task', taskId);

Previewing Options​

options() returns what the workflow would cost the current user for the given input, before running it — see the Options API:

const { credits, originalCredits, monetization } = await workflows.options('example-task', params);

Dismissing a Notification​

disableNotification(taskId) marks the task's notification as seen. Socket-mode runs do this automatically; call it yourself when you consume results another way (e.g. submit() + subscribe()).

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​

Every client method rejects with a single error class, WorkflowsError, exported from @picsart/workflows-client. Aborting is the only exception — it rejects with a DOMException (see Cancellation).

FieldDescription
messageThe error text, as returned by the API when the server answered.
reasonMachine-readable code, e.g. the API's own reason or one of the client reasons below.
httpStatusCodeThe HTTP status. undefined when the request never got an answer (e.g. connection_error).

Reasons set by the client itself:

reasonWhen
request_failedThe server returned an error response without its own reason.
client_timeoutASYNC polling used up retriesCount before the task finished (408).
connection_errorPolling couldn't reach the server after repeated retries.
invalid_responseThe server returned a body that isn't JSON.
workflow_failedThe workflow failed during a STREAM or SOCKET run.
invalid_stateThe client isn't set up for the call, e.g. no auth or no socket for SOCKET mode (400).
unknown_errorAny other unexpected failure.
import { WorkflowsError } from '@picsart/workflows-client';

try {
const { result } = await workflows.run<MyResponseType>('example-task', params);
} catch (err) {
if (err instanceof WorkflowsError) {
if (err.httpStatusCode === 402) {
// not enough credits
} else if (err.httpStatusCode === undefined || err.httpStatusCode >= 500) {
// connection or server failure — retry-friendly
}
}
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.