BuildBaseBuildBase

Webhook actions

Receive an HTTP call from a workflow and feed its response back into the next node.

actions.http_webhook calls your API from inside a workflow. The response is parsed and exposed as node outputs, so the next node can branch on your own service's answer.

Here is the endpoint that receives it:

// app/api/buildbase/provision/route.ts
export async function POST(request: Request) {
  const secret = request.headers.get('x-webhook-secret');
  if (secret !== process.env.WORKFLOW_WEBHOOK_SECRET) {
    return Response.json({ error: 'forbidden' }, { status: 403 });
  }

  const { workspaceId, plan } = await request.json();

  const account = await provisionAccount(workspaceId, plan);

  // Everything returned here is available to later nodes as
  // {{responseBody.<key>}}
  return Response.json({
    accountId: account.id,
    region: account.region,
    seatsRemaining: account.seatsRemaining,
  });
}

Before you start

Add the HTTP Webhook action in the console's workflow editor, set the URL, and add your shared secret under Headers. Header values accept merge tags.

Configuration

InputTypeDefaultNotes
urltextRequiredMerge tags allowed, so the path can include a workspace ID
methodselectPOSTPOST, PUT, PATCH only
headerskey-valueMerge tags allowed in values
bodyJSONMerge tags allowed with {{key}}
timeoutnumber30,000 msRange 1,000 – 120,000 ms

The console's method dropdown offers only POST, PUT, and PATCH. The action is built for sending data, so a read-only lookup against your API needs a POST endpoint on your side.

Content-Type: application/json is set for you. A string body that parses as JSON is sent as JSON; one that does not is sent as-is.

Constraints you cannot configure away

ConstraintValue
Request and response body cap5 MB each
Response stored on the nodeTruncated to 4,096 characters
Blocked destinationslocalhost, 127.x, 10.x, 172.16–31.x, 192.168.x, 169.254.x, 0.0.0.0, ::1
Stripped request headershost, authorization, cookie, x-forwarded-for, x-real-ip, proxy-authorization

Warning

authorization is silently stripped. Putting your credential in an Authorization header will not work — it is removed before the request is sent, with no error. Use a custom header name such as x-webhook-secret, as the example above does.

Destinations are checked twice: the hostname is matched against the blocked list, then resolved and its IP rejected if it lands in a private range. That second check defeats DNS rebinding, and it also means a workflow cannot call a service on your private network — the endpoint must be publicly reachable.

Outputs

OutputTypeDescription
statusCodenumberHTTP status returned
responseBodyobjectParsed response body
responseHeadersobjectResponse headers
successbooleanWhether the status was 2xx
durationnumberRound-trip time in ms
requestedAtstringISO timestamp of the request

Reference them downstream as {{responseBody.accountId}}, {{statusCode}}, and so on.

Branch on success rather than on statusCode unless you care about a specific code — success is already the 2xx test.

Outputs

OutputTypeDescription
statusCodenumberHTTP status returned
responseBodyobjectParsed response body
responseHeadersobjectResponse headers
successbooleanWhether the status was 2xx
durationnumberRound-trip time in ms
requestedAtstringISO timestamp of the request

Reference them downstream as {{responseBody.accountId}}, {{statusCode}}, and so on.

Branch on success rather than on statusCode unless you care about a specific code — success is already the 2xx test.

What counts as a failure

This is the part worth reading twice, because it is not what most people assume.

OutcomeNode resultRetried?
2xxCompletes, success: true
4xx or 5xxCompletes, success: false, statusCode setNo
TimeoutThrowsYes
DNS failure, connection refused, TLS errorThrowsYes
Blocked host or private IPThrowsYes

A non-2xx response does not fail the node. The request is treated as having happened — the status is simply recorded and the flow continues down the normal path with success: false. Retries exist for requests that never got an answer, not for answers you did not like.

Two consequences:

  • Returning 409 for "already processed" is fine. It will not be retried and it will not fail the instance. Branch on success or statusCode instead.
  • If you want a bad status to stop the flow, add an if_else condition on {{success}} after the webhook node. Nothing does this for you.

Retries

The retry policy applies only to the throwing cases above. This action overrides the workflow defaults, because outbound HTTP is the least reliable thing a workflow does:

SettingHTTP webhookWorkflow default
maxAttempts53
backoffTypeexponentialexponential
backoffDelay10,000 ms5,000 ms
timeoutMs60,000 ms30,000 ms

Note the two timeouts are different things: the timeout input (default 30s) bounds a single request, while timeoutMs in the retry policy (60s) bounds the attempt from the queue's point of view.

Warning

Five attempts with exponential backoff means your endpoint must be idempotent. A request that times out after doing its work is retried, and the workflow has no way to know the first attempt succeeded. Key your writes on something stable from the payload.

Testing

Running the workflow with /test in dry-run mode does not call your endpoint. The node logs what it would have sent and returns placeholder outputs. Use hot-run to exercise the real request.

Verifying the request

The action sends no signature — there is no HMAC header and no shared signing secret, unlike webhooks, which do sign their deliveries.

Authenticate the call yourself:

  • Put a long random secret in a custom header, as the example above does, and compare it in constant time.
  • Or embed an unguessable token in the URL path.

Do not rely on source IP. Workflow execution runs from the BuildBase workers and the address is not contractual.

Sending workflow context

The body is JSON with merge tags, so pass whatever the receiving service needs:

{
  "workspaceId": "{{workspaceId}}",
  "plan": "{{subscription.planName}}",
  "triggeredBy": "{{trigger.id}}"
}

Merge tag references are validated at publish time. A tag pointing at a node that no longer exists blocks the publish rather than silently sending null.

When the request never lands

After the fifth failed attempt — timeout, DNS, refused connection — the node fails. The instance then follows its error path if one is wired, or fails outright if not. Failed instances stay visible in the console and can be retried per node, so a fix on your side does not mean re-running the whole flow. See monitoring.

Next Steps