URL: /code/apps/sequences/create-funnel · Id: `page:/code/apps/sequences/create-funnel`

# Funnel.create

> Saves your path from landing page to opt-in to Sequence to offer, so each drop between steps is one number.

POST https://api.sb/&#123;your-domain&#125;/funnels

Saves your path from landing [page](https://startupbuilder.dev/business/nouns/page) to opt-in to [Sequence](https://startupbuilder.dev/business/nouns/sequence) to [offer](https://startupbuilder.dev/business/nouns/offer), so each drop between steps is one number.

- **Action class:** None
- **Approval:** Runs on its own
- **Emits:** `Funnel.created`
- **Price:** Free

```text title="Run it · Ask"
Save my path: the rent-ledger template page, the email opt-in, the 5-email course, then the Portfolio offer.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Funnel.create({ steps: [{ page: '/rent-ledger-template' }, { form: 'template-opt-in' }, { sequence: 'rent-ledger-course' }], offer: 'https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v' })
```

```json title="Run it · Response"
{
  "api": "https://api.sb/leaseline.app",
  "funnel": {
    "$id": "https://api.sb/leaseline.app/funnels/funnel_Rl6tG2dP4k",
    "$type": "Funnel",
    "steps": 3,
    "offer": "https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v"
  },
  "action": {
    "operation": "Funnel.create",
    "mode": "try",
    "status": "done"
  }
}
```

It extends the hub’s record call [`Funnel.create`](https://startupbuilder.dev/business/nouns/funnel/create), which every [Funnel](https://startupbuilder.dev/business/nouns/funnel) takes. It is one of the [Verbs](https://startupbuilder.dev/business/nouns/verb) of [Sequences](/code/apps/sequences#verbs).

## Inputs
| Name | Type | Meaning |
| --- | --- | --- |
| `steps` | `json` | Required. The pages, Forms, and Sequences in order. |
| `offer` | `Offer` | Each Funnel’s path ends at one offer, after its landing page, opt-in and Sequence. Optional. |

The hub inherits this call on every Noun: it takes the Funnel’s own Properties, listed on [the hub’s Funnel page](https://startupbuilder.dev/business/nouns/funnel).

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `funnel` | `Funnel` | Never null: the Funnel as the call leaves it. |

## Effects
**Action class**

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

**Modes**

- `try`: Runs every check and returns the record as it would be; nothing is stored.
- `send`: Returns a pending Action at once; `Funnel.created` fires when it is done.
- `do`: Waits for the Action to finish and returns the record.

**Idempotency**

Only with a key: the same `Idempotency-Key` and input return the first Action, and the same key with other input returns `409`.

**Saved or shared**

The Funnel is saved as the call leaves it, with the Action that changed it.

**Emits**

`Funnel.created` with `{ funnel }`

**Performed by**

Whichever seat calls it

**Price**

Free on your own account: the platform charges nothing for the call.

## On each Venue
It acts on your own records and reaches no Venue, so every account answers the same way.

## What try does
Runs every check the call meets and returns the record as it would be; nothing is stored, sent or posted.

## Refuses when
The hub sets no guard of its own on this call: it refuses only on the shared checks every write meets (a missing record, a Permission the seat lacks).

## When it waits
Returns in one response; nothing waits.

## Example
In `send` mode the call returns its pending Action at once, and `Funnel.created` delivers the result.

**Ask**

```text
Save my path: the rent-ledger template page, the email opt-in, the
5-email course, then the Portfolio offer.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/funnels?mode=send" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "steps": [
    {
      "page": "/rent-ledger-template"
    },
    {
      "form": "template-opt-in"
    },
    {
      "sequence": "rent-ledger-course"
    }
  ],
  "offer": "https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v"
}'
```

**CLI**

```bash
npx startup-builder do Funnel.create --send --data '{
  "steps": [
    {
      "page": "/rent-ledger-template"
    },
    {
      "form": "template-opt-in"
    },
    {
      "sequence": "rent-ledger-course"
    }
  ],
  "offer": "https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v"
}'
```

`--send` returns the pending Action at once.

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "send",
    "code": "return await $.Funnel.create({ steps: [{ page: '/rent-ledger-template' }, { form: 'template-opt-in' }, { sequence: 'rent-ledger-course' }], offer: 'https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v' })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.send.Funnel.create({
  steps: [{ page: '/rent-ledger-template' }, { form: 'template-opt-in' }, { sequence: 'rent-ledger-course' }],
  offer: 'https://api.sb/leaseline.app/offers/offer_Ls1mP4qR8v',
})
```


```json title="Response, in send mode"
{
  "mode": "send",
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/action_Sd3kQ9vT2w",
    "operation": "Funnel.create",
    "status": "pending",
    "reason": "running"
  }
}
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

## Used by
None.

## Related
[`Contact.enroll`](/code/apps/sequences/enroll) · [`Enrollment.pause`](/code/apps/sequences/pause) · [`Enrollment.resume`](/code/apps/sequences/resume) · [`Enrollment.cancel`](/code/apps/sequences/cancel) · [`Sequence.create`](/code/apps/sequences/create-sequence) · [`Sequence.update`](/code/apps/sequences/update)