Octov0.11.7
ReferenceConnectors

Parallel

Web search and asynchronous research runs, with verified webhooks.

The parallel connector holds the API key and talks to Parallel, a web research API built for agents. It has two shapes: search answers in the same request, while a task run is asynchronous, returning a handle and delivering the answer later as a webhook.

parallel connector

It is a service connector and provides no source. The blocks that bind to it are parallel-search, parallel-extract, parallel-task-run and parallel-verify-request. A task run's result arrives over an http source, since Parallel posts JSON to a route you own, and parallel-verify-request authenticates it.

Settings

SettingTypeRequiredDefaultDescription
apiKeystringYesnoneAuthenticates with the Parallel API; source it from ${PARALLEL_API_KEY}. Never logged.
webhookSecretstringNononeVerifies inbound webhooks. Take it from Settings → Webhooks on the Parallel platform, whsec_ prefix included.
timeoutdurationNo30sBounds each Parallel API call.

An apiBaseURL setting also overrides the API base (default https://api.parallel.ai), mainly for tests; it is not exposed in the editor.

samples/parallel-research uses every block on this page, and Async Research with Parallel walks through it.

env:
  - name: PARALLEL_API_KEY
    required: true

connectors:
  - name: research
    type: parallel
    settings:
      apiKey: ${PARALLEL_API_KEY}

A whsec_-prefixed secret carries base64 key material rather than the key itself, so the connector decodes it at startup. A malformed one fails the service immediately, rather than becoming a signature that silently never matches.

Error handling

Parallel signals errors with an HTTP status ≥ 400 carrying a detail field. Every block has a failOnError setting (default true): when true a Parallel error fails the flow, when false the message passes through unchanged.

Where the result goes

Every block follows the same convention as pinecone: the response becomes the body. Set resultVar to keep the incoming body and store the response in a variable instead.

parallel-search block

Parallel Search searches the web and returns ranked pages with LLM-optimized excerpts.

This is not a keyword API. An objective says, in natural language, what the caller is after; the queries are the searches to run toward it. Parallel ranks results against the objective, which is why both are required.

SettingTypeRequiredDefaultDescription
connectorstringYesnoneName of the parallel connector to use.
objectiveexpressionYesnoneCEL expression describing what the search is for.
searchQueriesexpressionYesnoneCEL expression for the queries to run: one string, or a list.
modeenumNoParallel's advancedadvanced (best quality), basic, fast, or turbo (~250ms).
maxResultsintNoParallel's 10Upper bound on the number of results (1–20).
maxCharsPerResultintNoParallel's defaultCap on the characters returned per result.
maxCharsTotalintNoParallel's defaultCap on the characters returned across every result together, the budget that matters when the results are headed for a model's context.
resultVarstringNononeStore the response here and leave the body; when empty, the response becomes the body.
failOnErrorboolNotrueTurn a Parallel API error into a flow error.
- type: parallel-search
  name: research
  settings:
    connector: research
    objective: body.question
    searchQueries: '[body.question]'
    mode: fast
    maxResults: 8
    maxCharsTotal: 20000
    resultVar: hits

maxResults and maxCharsPerResult are sent inside Parallel's advanced_settings object rather than at the top level of the request, and the block does that nesting for you. The request schema forbids unknown top-level fields, so getting it wrong fails the whole call rather than being ignored.

The response carries search_id, results (each with url, title, publish_date, and excerpts), plus warnings, usage, and session_id.

A setting left unset is omitted from the request, so Parallel's own default applies.

parallel-extract block

Parallel Extract reads the contents of specific web pages. Where search finds the pages, extract fetches what is on them.

Give it the urls to read. An objective is optional: with one, Parallel returns excerpts scoped to it; without one, it returns the page. Set fullContent to ask for the whole page instead.

SettingTypeRequiredDefaultDescription
connectorstringYesnoneName of the parallel connector to use.
urlsexpressionYesnoneCEL expression for the URLs to read: one string, or a list.
objectiveexpressionNononeCEL expression describing what to read the pages for. Scopes the excerpts Parallel returns.
fullContentboolNofalseReturn the whole page rather than objective-scoped excerpts.
resultVarstringNononeStore the response here and leave the body; when empty, the response becomes the body.
failOnErrorboolNotrueTurn a Parallel API error into a flow error.
- type: parallel-extract
  name: read
  settings:
    connector: research
    urls: body.urls
    objective: body.question
    resultVar: pages

The response carries extract_id, results (each with url, title, publish_date, excerpts, and full_content when fullContent was set), plus errors, warnings, usage, and session_id.

parallel-task-run block

Parallel Task Run starts one of Parallel's asynchronous research runs and returns its handle.

The block does not wait. What comes back is a receipt, {run_id, status, ...}, and the answer arrives later on the webhook. This block is the one exception to the connector's "result becomes the body" rule: the handle goes in a variable, so the flow that asked still has its own body to respond with.

SettingTypeRequiredDefaultDescription
connectorstringYesnoneName of the parallel connector to use.
processorstringYesnoneParallel processor to run the task on; it selects the depth/cost tier.
inputexpressionYesnoneCEL expression for the task input: a string, or an object matching the input schema.
outputSchemaexpressionNononeCEL expression for the output the task must produce: a JSON Schema object, or a plain-English description of the answer you want.
metadataexpressionNononeCEL expression for key/value metadata echoed back on the run and its webhook.
webhookURLstringNononeURL Parallel posts the run's result to.
eventTypesstring[]No[task_run.status]Event types to deliver to the webhook.
resultVarstringNoparallelRunVariable the run handle is stored in.
failOnErrorboolNotrueTurn a Parallel API error into a flow error.

The response carries run_id, status (queued, running, action_required, completed, failed, cancelling, or cancelled), is_active, processor, created_at, modified_at, metadata, warnings, and error.

- type: parallel-task-run
  name: start-research
  settings:
    connector: research
    processor: core
    input: body.question
    metadata: '{"correlationId": body.id}'
    webhookURL: ${PARALLEL_WEBHOOK_URL}

output_schema is a tagged union in Parallel's API, so the block wraps a JSON Schema object as {"type": "json", "json_schema": …}. A string is passed through untouched, which the API reads as a text schema:

    outputSchema: '"a one-paragraph summary with a source URL"'

Point webhookURL at an http source in this same service, and authenticate what arrives there. Use metadata to carry your own correlation id: it comes back on the webhook, which is how the callback finds the request that started the run.

Webhook verification

A task run's answer arrives as a webhook, a request from the open internet. Parallel signs each one following the Standard Webhooks spec, and parallel-verify-request checks that signature before the flow acts on the payload.

  1. Parallel sends three headers: webhook-id (unique per event), webhook-timestamp (unix seconds), and webhook-signature.
  2. The signed string is <webhook-id>.<webhook-timestamp>.<raw body>, HMAC-SHA256 with your secret's decoded key material, base64-encoded.
  3. webhook-signature carries that as v1,<base64>, or as a space-delimited list of them while a secret is being rotated, in which case any entry matching is enough.

The id and the timestamp are inside the signature, so a captured signature cannot be replayed under a different event id, and cannot be replayed at all outside a 5-minute window. The signature is over the exact request bytes, so a body re-serialized on the way no longer verifies, which is why the http source has to hand the block the raw bytes.

Unlike notion, there is no bootstrap handshake here: Parallel issues the secret in its dashboard, so it exists before the first webhook does. A parallel-verify-request in a flow whose connector has no webhookSecret fails to build.

parallel-verify-request block

Parallel Verify Request authenticates an inbound Parallel webhook delivered over the http connector, and aborts the flow on a bad or stale signature.

SettingTypeRequiredDefaultDescription
connectorstringYesnoneName of the parallel connector to use. Its webhookSecret is required.
idHeaderstringNowebhook-idVariable holding the webhook's unique id.
timestampHeaderstringNowebhook-timestampVariable holding the webhook's unix timestamp.
signatureHeaderstringNowebhook-signatureVariable holding the webhook's signature.
rawBodyVarstringNorawBodyVariable holding the exact request body; must match the http source's rawBodyVar. Optional when the http source uses raw-content mode (rawBody: true), where the block reads the raw body directly and re-parses it into body.

The http source must expose the exact request bytes, either with rawBodyVar: rawBody or with rawBody: true, and copy all three headers into variables:

source:
  connector: api
  type: http
  settings:
    path: /parallel/events
    methods: [POST]
    headers: [webhook-id, webhook-timestamp, webhook-signature]
    rawBody: true
- type: parallel-verify-request
  name: verify
  settings:
    connector: research

The event payload is {timestamp, type, data}, where type is task_run.status and data is the full task run object. Correlate it with the request that started the run through the metadata you passed to parallel-task-run, which comes back on the run.

On this page