# Pipedream

> BlueBot’s Pipedream components send texts from your own number and start workflows when a text comes in. Every action and source, and how to call BlueBot from your own code steps.

BlueBot on Pipedream is an app you connect once, a set of actions (Send Text, Schedule Text and the rest) and instant sources that start a workflow when BlueBot posts an event. Because Pipedream runs code too, you can also call BlueBot from a Node.js or Python step with the connected account, which is the quickest way to do something the actions don’t.

Texts sent from Pipedream go through your sending rules like every other text and show `source: "pipedream"` in BlueBot.

## Connect your account

### Make a key

In BlueBot, open **Developers → API keys**, press **New key**, name it “Pipedream” and tick every scope. Sources need `webhooks:manage`, the contact actions need `contacts:read` and `contacts:write`, and the connection test needs `account:read`. Copy the key.

### Connect it in Pipedream

Add any BlueBot step or source, press **Connect a BlueBot account**, paste the key into **API Key** and press **Save**. Pipedream tests it with `GET /v1/me`.

The connected account is the `bluebot` app prop: every component and code step that names it gets your key as `this.bluebot.$auth.api_key`, and Pipedream keeps the key out of your workflow’s code and logs.

## Sources

Each source is instant. When you deploy it, it makes a webhook in your BlueBot account pointing at the source’s own HTTP endpoint, subscribed to the events it needs, and keeps the webhook’s id and signing secret in its `$.service.db`. When you delete the source, it deletes the webhook. The webhook shows under **Developers → Webhooks** in BlueBot, described as “Pipedream: ” and the source’s name.

Every request is checked against `webhook-signature` with that secret before anything is emitted, and one that fails is answered 401. Each emitted event uses BlueBot’s event id as its id with `dedupe: "unique"`, so a delivery BlueBot retries never starts your workflow twice.

On deploy, a source also emits up to 10 recent matching events from `GET /v1/events`, so you have real data to build the next steps with.

| Source                        | Key                                   | Emits                                                                                                  | BlueBot events                                                                                                                      |
| ----------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| New Incoming Text (Instant)   | `bluebot-new-incoming-text-instant`   | Each text somebody sends you, as the message.                                                          | `message.received`                                                                                                                  |
| Text Status Changed (Instant) | `bluebot-text-status-changed-instant` | Each change of state on a text you sent. Prop **States** picks which.                                  | `message.sent`, `message.delivered`, `message.read`, `message.failed`, `message.unconfirmed`, `message.expired`, `message.canceled` |
| New Contact (Instant)         | `bluebot-new-contact-instant`         | Each person added to your contacts.                                                                    | `contact.created`                                                                                                                   |
| Contact Stopped (Instant)     | `bluebot-contact-stopped-instant`     | Each person who goes on your stop list.                                                                | `contact.stopped`                                                                                                                   |
| Sequence Completed (Instant)  | `bluebot-sequence-completed-instant`  | Each person who reaches the end of a sequence or leaves it early. Prop **Sequence** narrows it to one. | `sequence.completed`                                                                                                                |

The event a source emits is the record (`steps.trigger.event` is the message, contact or enrollment, exactly as the API returns it), and the summary names it, such as “New text from +15555550123”. The envelope’s `id` is `steps.trigger.context.id` and its `timestamp` is `steps.trigger.context.ts`, in milliseconds. The record carries the rest: a message’s `state` says which change it was.

New Incoming Text hears the texts of people you’ve texted through BlueBot, unless you set **Which chats to copy** to **Everything** in **Settings → Sending rules**. Its props are **Include Group Chats** (default true) and **Only From** (a number or email address).

## Actions

| Action                   | Key                                | What it does                                                      |
| ------------------------ | ---------------------------------- | ----------------------------------------------------------------- |
| Send Text                | `bluebot-send-text`                | Sends a text now, or as soon as your sending rules allow.         |
| Send Template            | `bluebot-send-template`            | Sends one of your templates with the variables you give it.       |
| Schedule Text            | `bluebot-schedule-text`            | Sends a text at a time. It can be changed or canceled until then. |
| Cancel Scheduled Text    | `bluebot-cancel-scheduled-text`    | Cancels a scheduled or queued text by its `msg_` id.              |
| Create or Update Contact | `bluebot-create-or-update-contact` | Finds a person by number or email and updates them, or adds them. |
| Add Tag                  | `bluebot-add-tag`                  | Adds tags to a person.                                            |
| Enroll in Sequence       | `bluebot-enroll-in-sequence`       | Starts a person on a sequence.                                    |
| Remove from Sequence     | `bluebot-remove-from-sequence`     | Takes a person off one sequence or all of them.                   |
| Find Contact             | `bluebot-find-contact`             | Finds a person by number or email.                                |
| Find Conversation        | `bluebot-find-conversation`        | Your conversation with a person and its newest text.              |
| Check iMessage           | `bluebot-check-imessage`           | Whether your newest chat with a number used iMessage or SMS.      |

### Send Text props

| Prop       | Type                                      | Required                 | What it is                                                                                                       |
| ---------- | ----------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| To         | string                                    | Unless Group Chat is set | A mobile number in any format (US numbers can leave off +1), or an iMessage email address.                       |
| Group Chat | string, options from your group chats     | No                       | Text an existing group chat instead.                                                                             |
| Text       | string                                    | Unless Files is set      | Up to 2,000 characters.                                                                                          |
| Files      | string\[]                                 | No                       | Up to 10 file URLs or paths in `/tmp`. Each is uploaded to BlueBot first, up to 25 MB.                           |
| Service    | string, options `auto`, `imessage`, `sms` | No                       | `auto` by default: the newest chat’s service, else iMessage, with one SMS retry for an iMessage that never went. |
| Expires At | string (ISO 8601)                         | No                       | Give up if it’s still waiting then. Default: 24 hours after it’s due.                                            |
| Metadata   | object                                    | No                       | Up to 20 keys; echoed on every webhook about the text.                                                           |

Schedule Text adds **Send At** (required, ISO 8601). Send Template takes **To**, **Template** (options from your templates), **Variables** (object), **Service** and **Metadata**. A variable you leave out comes from the contact or your business name, else from the template’s own fallback, and one with none of those stops the send with `invalid_request`, naming it. Find Conversation takes **Phone or Email** and returns `conversation_id`, `contact_id`, `last_message_id`, `last_message_text`, `last_message_direction`, `last_message_state`, `last_message_at` and `waiting_on_you`, or nothing when you’ve never texted them. The contact and sequence actions take the fields of the same names on the [Zapier page](https://usebluebot.com/docs/zapier), as props.

Each action exports a summary, such as “Queued a text to +15555550123” or, for a held text, “Accepted a text to +15555550123; it waits for quiet hours to end”, and returns the record the API answered. Every action that sends a text carries an `Idempotency-Key` made from the workflow’s event, the action and what the step was given, so Pipedream retrying the step after an error doesn’t send twice.

## Your own code steps

Add the `bluebot` app prop to a Node.js step and use the TypeScript SDK, which Pipedream installs from the import:

```js title="Node.js step"
import BlueBot from "@usebluebot/sdk";

export default defineComponent({
  props: {
    bluebot: { type: "app", app: "bluebot" },
  },
  async run({ steps, $ }) {
    const client = new BlueBot({ apiKey: this.bluebot.$auth.api_key });
    const lead = steps.trigger.event;

    const message = await client.messages.send(
      {
        to: lead.phone,
        text: `Hi ${lead.first_name}, thanks for reaching out. When is a good time to talk?`,
        metadata: { source: "pipedream", lead_id: String(lead.id) },
      },
      // The same trigger event never sends twice, even when Pipedream retries this step.
      { idempotencyKey: `${steps.trigger.context.id}-welcome` },
    );

    $.export("$summary", `Text to ${message.to} is ${message.state}`);
    return message;
  },
});
```

Or the Python SDK in a Python step:

```python title="Python step"
from bluebot import BlueBot

def handler(pd: "pipedream"):
    client = BlueBot(api_key=pd.inputs["bluebot"]["$auth"]["api_key"])
    lead = pd.steps["trigger"]["event"]

    message = client.messages.send(
        to=lead["phone"],
        text=f"Hi {lead['first_name']}, thanks for reaching out.",
        idempotency_key=f"{pd.steps['trigger']['context']['id']}-welcome",
    )
    return message.model_dump(mode="json", by_alias=True)
```

The [TypeScript](https://usebluebot.com/docs/sdks/typescript) and [Python](https://usebluebot.com/docs/sdks/python) pages list every method.

## Test a workflow

### Use your own number first

In the action’s **To**, type your own mobile number, and press **Test**.

| You got                                                      | It means                                   | Do next                                                         |
| ------------------------------------------------------------ | ------------------------------------------ | --------------------------------------------------------------- |
| A message with `state: "queued"`, and the text on your phone | It works.                                  | Put the real value back in **To** and deploy.                   |
| `hold.reason: "quiet_hours"`                                 | It’s quiet hours, so it waits for morning. | Nothing, or change quiet hours in **Settings → Sending rules**. |
| `hold.reason: "device_offline"`                              | Your Mac isn’t checking in.                | Wake it; the text goes once it’s back.                          |
| `invalid_request`                                            | A field didn’t fit; the message names it.  | Fix that prop. [More](https://usebluebot.com/docs/errors/invalid_request)             |

### Check a source

Deploy the source, then text your BlueBot number from another phone you’ve texted through BlueBot before. The event shows in the source’s list within a second or two.

## When a step fails

Actions throw BlueBot’s error with its message, hint, code and request id, and Pipedream shows it on the step. `rate_limited` (429) and the 5xx errors are worth retrying: turn on **Auto-retry** in the workflow’s settings, and the idempotency key keeps a retried send from going twice. A held text, and a text that fails after it was accepted, are not errors; use **Text Status Changed** to hear about those.
