# HTTP Trigger

Trigger a workflow from any external HTTP request.

The HTTP trigger exposes a unique public URL for your workflow. Any system that can send an HTTP request — Zapier, Make, another service, your own backend — can trigger it.

## The trigger URL

Each workflow gets a unique URL shown in the editor under **Trigger → HTTP Trigger**. The URL includes a secret token that authenticates the request, so you do not need to add your own authentication unless you want additional validation.

> Caution: Treat your trigger URL as a secret. Anyone with the URL can trigger your workflow.

## The `data` object

```js
{
  // Parsed JSON body (or null if the body is not valid JSON)
  ...bodyFields,

  // URL query parameters are merged into data at the top level
  // e.g. ?foo=bar makes data.foo === 'bar'
}
```

The entire parsed JSON body is spread into `data` at the top level. Query string parameters are also available on `data`. You can access them directly:

```js
export class Workflow {
  async start(data, headers, api) {
    console.log('Order ID from body:', data.orderId);
    console.log('Source from query string:', data.source);
  }
}
```

## Returning a custom response

The return value of your `start` step becomes the HTTP response sent back to the caller.

**Return a plain object** — serialised as JSON with `Content-Type: application/json`:
```js
async start(data, headers, api) {
  // ... do work ...
  return { received: true, id: data.orderId };
}
// Caller receives: {"received":true,"id":"12345"}
```

**Return a `Response` object** — full control over status, headers, and body:
```js
async start(data, headers, api) {
  if (!data.orderId) {
    return new Response('Missing orderId', { status: 400 });
  }
  // ... do work ...
  return new Response('Accepted', { status: 202 });
}
```

**Return nothing** — caller receives `{"success":true}`:
```js
async start(data, headers, api) {
  // fire and forget
}
```

> Note: Only the `start` step's return value is sent to the caller. Any subsequent steps scheduled with `api.scheduleNextStep()` run asynchronously **after** the response has already been sent.

## Example — validate and enqueue

```js
export class Workflow {
  async start(data, headers, api) {
    if (!data.orderId || !data.action) {
      return new Response(
        JSON.stringify({ error: 'Missing required fields' }),
        { status: 400, headers: { 'content-type': 'application/json' } }
      );
    }

    // Schedule heavy processing asynchronously
    await api.scheduleNextStep({
      delay: 10,
      action: 'processOrder',
      payload: { orderId: data.orderId, action: data.action }
    });

    return { queued: true, orderId: data.orderId };
  }

  async processOrder(data, headers, api) {
    // This runs 10 seconds later, after the HTTP response was already returned
    console.log('Processing order', data.orderId);
  }
}
```