en

Connect Crisphive to Pipedream

By the end of this guide a Pipedream workflow can book and confirm a field job in Crisphive in one step, look up a caller by phone, and start automatically — with a verified signature — when a job is completed or a customer is created.

Pipedream runs workflows: a trigger starts them and steps do the work, either ready-made actions or short Node.js code. Crisphive is a field operations platform that schedules your technicians with constraint-based scheduling on a deterministic solver.

Three words you will meet below, in plain language: an API key is a long password that lets a program act for your business in Crisphive. A webhook is Crisphive calling a web address when something happens, so your workflow hears about it instantly. An HTTP request is a program calling a web address to read or change something — here, Pipedream calling Crisphive.

This guide uses Pipedream’s built-in HTTP / Webhook trigger, HTTP request and Node.js steps, calling the Crisphive public API directly.

Note on Pipedream Workflows. Pipedream stopped accepting new sign-ups for Workflows on 30 September 2026 and has announced that Workflows will shut down on 31 March 2027. Pipedream Connect and Pipedream’s MCP server are not affected. If you are choosing a tool today, the same Crisphive automations are available in n8n; for AI agents, connect directly to the Crisphive MCP server (see below).

What you can do

KindPipedream stepCrisphive API
ActionBook and confirm a job — HTTP request or Node.jsPOST /v1/job-requests/book-and-confirm: creates the job, sizes it from the job type’s default duration and confirms it at your chosen time, with Crisphive picking the technician.
SearchFind a customer by phone — HTTP requestGET /v1/customers?phone=+16135550177: exact match on the number.
ActionCreate a customer — HTTP requestPOST /v1/customers
ActionGet a job — HTTP requestGET /v1/job-requests/{id}: status, schedule, technician, customer, address.
TriggerHTTP / Webhook trigger + a Node.js signature checkCrisphive calls your workflow on events you subscribe to with POST /v1/webhooks (job completed, job confirmed, customer created, …).

Before you start

  • A Crisphive account with Developer access — by default only the business Owner has it. It is needed to create API keys, including one with the trigger scopes.
  • An existing Pipedream account that can create workflows (see the note above about new sign-ups).
  • Every job type you will book has a default duration (Crisphive → Settings → Job Types). The built-in “General” type already has one (60 minutes) and is used when you do not choose a type.
  • About 20 minutes for the booking action, 15 more for the trigger. No programming experience is needed: every piece of code below is copy-and-paste.

Step 1 — Create a Crisphive API key

Start with a test key (it begins with chsk_test_). It works only on your business’s Rehearsal copy — the sandbox, with test data — so nothing you do reaches a real customer or technician.

1
Switch to Rehearsal

In the left sidebar of the Crisphive dashboard, click Rehearsal in the Rehearsal | Live switch, then click Switch. Rehearsal is Crisphive’s sandbox: isolated test data, and no real customer or technician is ever texted or emailed. A key created while you are in Rehearsal starts with chsk_test_ and only ever touches the test data.

2
Open API Keys

Go to Settings → Developer → API Keys (crisphive.com/app/settings/developer/api-keys) and click Create new API key. Creating keys needs Developer access, which by default only the Owner has.

3
Choose Secret and name the key

Key type: Secret. Never Publishable — a publishable chpk_ key is only for the booking widget on your website and is refused everywhere else. Name: Pipedream.

4
Choose how long it lasts

In Expires in (days) keep 30 days for a test key. Keys always expire: 30 days by default, anything from 1 to 365 days chosen when you create the key. The Owner and Administrators are emailed 7 days before a key expires.

5
Restrict it to booking

Under Scopes choose Restricted and select customers_view, customers_manage, job_view, job_create and job_manage. If you will use a trigger, also select developer_view and developer_manage_api_keys (subscribing and unsubscribing a webhook needs them), plus team_view if you want technician events.

6
Create and copy

Click Create API key, then Copy, then Done. The full key is shown only once — paste it into your password manager. If you lose it, create a new key and revoke the old one.

Step 1 of 6In Settings → Developer → API Keys, click Create new API key. Keep Key type on Secret — never Publishable.

All steps
  1. In Settings → Developer → API Keys, click Create new API key. Keep Key type on Secret — never Publishable.
  2. Type the name Pipedream so you recognise the key later.
  3. Keep Expires in (days) on 30 days for a test key. You can choose up to 365 days.
  4. Under Scopes choose Restricted and select customers_view, customers_manage, job_view, job_create, job_manage.
  5. Click Create API key.
  6. Click Copy. The key starts with chsk_test_ and is never shown again. Then click Done.

Step 2 — Store the key in Pipedream

Keep the key out of your workflow steps by saving it as a Pipedream environment variable — a named secret every step can read as process.env.FIELD_OPS_API_KEY, or as {{process.env.FIELD_OPS_API_KEY}} in a form field.

1
Open environment variables

In Pipedream, open Settings → Environment Variables for the whole workspace, or open your project and click Variables in its left navigation for that project only. Click New Variable.

2
Add the key

Set Key to FIELD_OPS_API_KEY and Value to your chsk_test_… key. Leave Secret on (the default) so the value stays hidden, and save.

Step 1 of 3Name the variable FIELD_OPS_API_KEY.

All steps
  1. Name the variable FIELD_OPS_API_KEY.
  2. Paste your chsk_test_… key as the value. It is hidden once saved.
  3. Click Save.

Step 3 — Book and confirm a job

Add a step to your workflow after its trigger (a form submission, a new spreadsheet row, a phone-system event…). You can build the request with Pipedream’s HTTP request form (option A) or paste a Node.js step (option B). Both send the same call.

Option A — HTTP request form.

1
Add an HTTP request step

Click + after the trigger, choose HTTP / Webhook, then the action that sends an HTTP request.

2
Method and URL

Method POST, URL https://api.crisphive.com/v1/job-requests/book-and-confirm.

3
Headers

Authorization = Bearer {{process.env.FIELD_OPS_API_KEY}} (the word Bearer, one space, the key). Content-Type = application/json. Idempotency-Key = pd-{{steps.trigger.context.id}}-book — see why.

4
Body

Choose a raw JSON body and paste the template below, replacing the values with data from your trigger (for example {{steps.trigger.event.body.phone}}).

5
Test

Click Test. A green result with "error_code": 0 and "confirmed": true means the job is booked.

Step 1 of 5Set the method to POST and the URL to the book-and-confirm endpoint.

All steps
  1. Set the method to POST and the URL to the book-and-confirm endpoint.
  2. Add the Authorization header: Bearer, a space, then the environment variable.
  3. Add Content-Type: application/json and an Idempotency-Key built from the trigger’s event ID.
  4. Paste the JSON body: the customer, the service address, the start time on the business’s clock and a description.
  5. Click Test. The response shows error_code 0, confirmed: true and a short_code.

The body, ready to copy:

{
  "customer": {
    "full_name": "Alex Martin",
    "phone": "+16135550177",
    "email": "alex.martin@example.com"
  },
  "address": {
    "line": "145 Laurier Avenue West",
    "city": "Ottawa",
    "state": "ON",
    "postal_code": "K1P 5J3",
    "country": "CA"
  },
  "scheduled_at": "2026-10-06T10:00:00",
  "description": "Furnace stopped heating"
}
FieldRequiredMeaning
customerYes (or customer_id)full_name plus a phone (E.164, with + and country code) or an email. Crisphive matches an existing customer by phone or email and creates one only if there is no match — never a duplicate. Add "sms_opt_in": true only if the customer explicitly agreed to texts.
addressYes for a new callerWhere the work happens. line plus a city or postal_code, so Crisphive can find a technician who covers that area.
scheduled_atYesStart time on the business’s own clock, with no timezone offset: 2026-10-06T10:00:00. Never add Z or -04:00.
customer_idInstead of customerAn existing customer’s ID. Crisphive then uses the address stored on the customer; one with no stored address is refused with JOB_REQUEST_ADDRESS_REQUIRED. Sending customer + address is safer.
job_type_idOptionalEmpty = your default job type (“General”). Get IDs from GET /v1/job-types.
job_duration_minutesOptionalEmpty = the job type’s default duration.
descriptionOptionalThe problem in the customer’s words (up to 2,000 characters).

A successful answer:

{
  "error_code": 0,
  "message": "Success",
  "data": {
    "job_id": "7b2f0c1e-3a44-4d1b-9a5e-0e6f1c2d3b4a",
    "short_code": "REQ-CA-YCNFQUUV",
    "customer_id": "c4d1a9e2-8f3b-4c6d-a1e2-5f7b9c0d1e2f",
    "confirmed": true,
    "scheduled_at": "2026-10-06T14:00:00Z",
    "assigned_technician_id": "1e9d7c5b-3a2f-4e1d-8c7b-6a5f4e3d2c1b"
  }
}
  • error_code 0 means success; anything else is a stable error code (see Troubleshooting).
  • scheduled_at in the answer is UTC — 14:00 UTC is 10:00 in Ottawa in October.
  • If data.confirmed is false, the job was saved but no technician can take it at that time. data.refusal.error_code says why (for example JOB_REQUEST_NO_TECHNICIAN_AVAILABLE), and the job waits in the coordinator’s queue. Notify a person; do not send the booking again.

Option B — Node.js step. Add a Node.js code step and paste:

export default defineComponent({
  async run({ steps, $ }) {
    // Replace these with data from your trigger, e.g. steps.trigger.event.body.phone
    const booking = {
      customer: {
        full_name: "Alex Martin",
        phone: "+16135550177",
        email: "alex.martin@example.com",
      },
      address: {
        line: "145 Laurier Avenue West",
        city: "Ottawa",
        state: "ON",
        postal_code: "K1P 5J3",
        country: "CA",
      },
      scheduled_at: "2026-10-06T10:00:00", // business local time, no offset
      description: "Furnace stopped heating",
    };

    const res = await fetch("https://api.crisphive.com/v1/job-requests/book-and-confirm", {
      method: "POST",
      headers: {
        Authorization: "Bearer " + process.env.FIELD_OPS_API_KEY,
        "Content-Type": "application/json",
        // Same event => same key => a retry never books twice.
        "Idempotency-Key": "pd-" + steps.trigger.context.id + "-book",
      },
      body: JSON.stringify(booking),
    });
    const envelope = await res.json();
    if (envelope.error_code !== 0) {
      throw new Error("Crisphive " + envelope.error_code + ": " + envelope.message);
    }
    const job = envelope.data;
    $.export("$summary", job.confirmed
      ? "Booked " + job.short_code
      : "Saved " + job.short_code + " — not yet scheduled (" + job.refusal?.error_code + ")");
    return job;
  },
});

Step 4 — Find a customer by phone

1
Add an HTTP request step

Method GET, URL https://api.crisphive.com/v1/customers, header Authorization = Bearer {{process.env.FIELD_OPS_API_KEY}}.

2
Add the phone as a query parameter

Parameter phone = the number in E.164, for example +16135550177 (Pipedream encodes the + for you when you use the parameters list).

3
Read the result

data.customers is the list of matches. One entry: you found the caller (use data.customers[0].id). Empty: a new caller. Each entry has id, full_name, tier, status, request_count and last_request_at.

Step 1 of 3Set Method to GET and URL to https://api.crisphive.com/v1/customers.

All steps
  1. Set Method to GET and URL to https://api.crisphive.com/v1/customers.
  2. Add the query parameter phone with the caller’s number in E.164.
  3. Add the Authorization header and click Test.
The match is exact. A number without the country code (6135550177) is refused with 400 PHONE_INVALID, never answered with an empty list — so an empty list really means “new caller”. To create a customer without booking, POST https://api.crisphive.com/v1/customers with {"full_name": "…", "phone": "+1…"} and an Idempotency-Key; the answer is {"customer_id": "…"}.

Step 5 — Start a workflow on a Crisphive event

Here you create a workflow with an HTTP address, tell Crisphive to call that address on the events you choose, and add a step that checks every call really comes from Crisphive.

1
Create the receiving workflow

New workflow → trigger HTTP / Webhook → New Requests. Keep the default response (HTTP 200 straight away). Configure it to pass the raw request body to the workflow if offered — the signature is computed over the exact bytes Crisphive sent. Copy the trigger’s URL (it looks like https://eo….m.pipedream.net).

2
Subscribe in Crisphive

Call POST https://api.crisphive.com/v1/webhooks once with that URL and the events you want — from a terminal with the command below, or from a one-off Node.js step. Ask for "expires_in_days": 365: the default is 30 days, after which the subscription stops.

3
Check the answer

The answer’s data.status must be active. Crisphive sent a test ping to your URL while creating it; Pipedream answered 200, so the subscription is live. Copy data.id (to remove it later) and data.secret (whsec_…, shown once).

4
Store the secret

Add a second environment variable, FIELD_OPS_WEBHOOK_SECRET, with the whsec_… value.

5
Add the verification step

Right after the trigger, add a Node.js step with the verification code below. It stops the workflow for the ping and for anything not signed by Crisphive, and returns the event for the next steps.

6
Deploy

Deploy the workflow. Every matching Crisphive event now runs it within seconds.

Subscribe from a terminal (replace the URL and key):

curl -X POST "https://api.crisphive.com/v1/webhooks" \
  -H "Authorization: Bearer chsk_test_4eC8xQ9mZ2pL7Ka0rT" \
  -H "Idempotency-Key: pipedream-subscribe-2026-10-04" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://eo1234567890abc.m.pipedream.net",
    "event_types": ["job_request.completed", "customer.created"],
    "description": "Pipedream workflow",
    "expires_in_days": 365
  }'

Or from a Node.js step you run once with Test (then delete the step):

export default defineComponent({
  async run({ $ }) {
    const res = await fetch("https://api.crisphive.com/v1/webhooks", {
      method: "POST",
      headers: {
        Authorization: "Bearer " + process.env.FIELD_OPS_API_KEY,
        "Content-Type": "application/json",
        "Idempotency-Key": "pipedream-subscribe-" + Date.now(),
      },
      body: JSON.stringify({
        url: "https://eo1234567890abc.m.pipedream.net", // your trigger URL
        event_types: ["job_request.completed", "customer.created"],
        description: "Pipedream workflow",
        expires_in_days: 365,
      }),
    });
    const envelope = await res.json();
    if (envelope.error_code !== 0) {
      throw new Error("Crisphive " + envelope.error_code + ": " + envelope.message);
    }
    // Copy id and secret now: the secret is never shown again.
    return { id: envelope.data.id, status: envelope.data.status, secret: envelope.data.secret };
  },
});

The verification step (paste as the first Node.js step after the HTTP trigger):

import { createHmac, timingSafeEqual } from "node:crypto";

// Crisphive-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]
// v1 = HMAC-SHA256 of "<t>.<raw body>" with the whsec_ secret.
// Accept if ANY v1 matches (two are sent for 24 h after a secret rotation)
// and t is within 5 minutes of now.
function verify(rawBody, header, secret, toleranceSec = 300) {
  if (!header || !secret || rawBody == null) return false;
  let t = "";
  const sigs = [];
  for (const part of String(header).split(",")) {
    const i = part.indexOf("=");
    if (i < 0) continue;
    const k = part.slice(0, i).trim();
    const v = part.slice(i + 1).trim();
    if (k === "t") t = v;
    else if (k === "v1") sigs.push(v);
  }
  const ts = Number(t);
  const now = Math.floor(Date.now() / 1000);
  if (!t || !Number.isFinite(ts) || Math.abs(now - ts) > toleranceSec || sigs.length === 0) return false;
  const body = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(String(rawBody), "utf8");
  const want = createHmac("sha256", secret).update(t + ".").update(body).digest();
  return sigs.some((s) => {
    const got = Buffer.from(s, "hex");
    return got.length === want.length && timingSafeEqual(got, want);
  });
}

export default defineComponent({
  async run({ steps, $ }) {
    const event = steps.trigger.event;
    const raw = event.bodyRaw ?? (typeof event.body === "string" ? event.body : undefined);
    const body = typeof event.body === "string" ? JSON.parse(event.body) : event.body;

    // The ping arrives while the subscription is being created, before you
    // have the secret. It is never a real event: stop here.
    if (body?.type === "ping") return $.flow.exit("Crisphive ping — nothing to do");

    if (raw === undefined) {
      throw new Error("No raw body: set the HTTP trigger to pass the raw request body");
    }
    const headers = event.headers ?? {};
    const signature = Object.entries(headers)
      .find(([name]) => name.toLowerCase() === "crisphive-signature")?.[1];
    if (!verify(raw, signature, process.env.FIELD_OPS_WEBHOOK_SECRET)) {
      return $.flow.exit("Invalid Crisphive-Signature — ignored");
    }

    // { id, type, created_at, business_id, environment, data: { object } }
    return { event_id: body.id, type: body.type, object: body.data?.object ?? {} };
  },
});
  • Job events are thin. A job_request.* event carries the job’s id, short_code and status. Add a GET request to https://api.crisphive.com/v1/job-requests/{{steps.verify.$return_value.object.id}} (replace verify with your step’s name) to read the customer, address, schedule and technician.
  • Repeats. Crisphive delivers at least once. Use event_id to skip an event you already handled.
  • Events you can subscribe to: GET /v1/webhooks/event-types lists them — job_request.created, .confirmed, .assigned, .rescheduled, .completed, .archived, .status_changed, .priority_changed, customer.created / .updated / .deleted, technician.created / .updated / .deleted.
  • Scopes. Subscribing (POST /v1/webhooks) and unsubscribing (DELETE) need developer_manage_api_keys; listing event types needs developer_view. Each event family also needs read access: customer.* needs customers_view, job_request.* needs job_view, technician.* needs team_view; otherwise 403 WEBHOOK_EVENT_NOT_PERMITTED.
  • Turning it off. When you no longer need the trigger, call DELETE https://api.crisphive.com/v1/webhooks/{id} with the same key, or delete the endpoint in Crisphive → Settings → Developer → Webhooks. A business can have at most 25 active webhooks per environment.
  • Lifetime. With expires_in_days: 365 the secret lasts a year; Crisphive emails the Owner 7 days before. Subscribe again (and store the new secret) before then. A subscription also stops when the key that created it is revoked.
If your Pipedream trigger cannot pass the raw body, do not verify against a re-serialised copy of the JSON — it will not match. Instead treat each event as a signal only: read the job back with GET /v1/job-requests/{id} using your API key and act on what Crisphive returns, never on the event body.

AI agents: Pipedream Connect, MCP and the lasting path

If what you want is an AI assistant that books and manages jobs, you do not need a Pipedream workflow at all. Crisphive runs its own MCP server at https://api.crisphive.com/mcp — MCP (Model Context Protocol) is the standard AI assistants use to discover and call tools. Point any MCP client at it with the header Authorization: Bearer chsk_…, or sign in to Crisphive with OAuth and approve — the connection then acts as the member who approved, with that member’s role. Smaller tool sets: /mcp/voice (caller lookup and one-call booking), /mcp/crm, /mcp/dispatch. See MCP server.

Worked workflows

  • Web form → confirmed job. HTTP trigger receiving your form → the Node.js booking step from Step 3 with steps.trigger.event.body fields → if confirmed is true, send the customer an email with short_code; otherwise post “needs a time” to your team chat.
  • Caller lookup. Phone-system trigger → Step 4 lookup → if one customer, book with their phone and the address they give; if none, book with full name, phone and address (Crisphive creates the customer).
  • Job completed → review request. HTTP trigger subscribed to job_request.completed → verification step → GET the job → email customer.email with your review link.
  • New customer → spreadsheet. HTTP trigger subscribed to customer.created → verification step → append object.full_name, object.phone, object.email and event_id to a sheet.

Retries never book twice

The Idempotency-Key header is a label meaning “this is the same request as before”. Built from steps.trigger.context.id (the ID Pipedream gives each incoming event), it stays the same if Pipedream retries the step for that event, and Crisphive replays the first answer instead of booking again. A new event gets a new ID and is a new booking. Never build the key from the current time in a booking step — every retry would then book a second job. Sending the same key with a different body is refused with 422 IDEMPOTENCY_KEY_REUSE.

Test it

Use these fictional details. The 555-01xx range and example.com are reserved for fiction, and Crisphive never texts or emails them.

1
Look up the caller

Run the Step 4 request with phone=+16135550177. Success: error_code 0 and customers is empty (a new caller) or holds Alex Martin.

2
Book

Run the Step 3 request with the body shown (Alex Martin, +16135550177, alex.martin@example.com, 145 Laurier Avenue West, Ottawa, ON K1P 5J3, CA, “Furnace stopped heating”) and a weekday-morning scheduled_at inside your working hours. Success: confirmed: true and a short_code.

3
Retry safely

Run the same step again with the same Idempotency-Key. Success: the identical answer — still one job in Crisphive.

4
Check Crisphive

Switch the dashboard to Rehearsal. The job appears on the board with status Confirmed (opened, its window is titled Booking Confirmed), a technician assigned and the Laurier Avenue address.

5
Fire the trigger

Complete that Rehearsal job on the dashboard. Success: within seconds your receiving workflow runs, and the verification step returns type: "job_request.completed".

Troubleshooting

SymptomCauseFix
401 API_KEY_INVALIDKey mistyped or revoked, “Bearer ” missing, or a publishable chpk_ key.Header must read Bearer chsk_test_… exactly; check FIELD_OPS_API_KEY.
401 API_KEY_EXPIREDThe key reached the end of its lifetime.Create a new key and update the environment variable.
403 API_KEY_SCOPE_INSUFFICIENTThe key lacks a permission; data.required_scope names it. On POST or DELETE /v1/webhooks it is developer_manage_api_keys; on the event-types list, developer_view.Create a key that includes it — for triggers, add developer_view and developer_manage_api_keys to the booking scopes.
403 WEBHOOK_EVENT_NOT_PERMITTEDThe key cannot read that event type; the message names the permission.Create a key that also has it — team_view for technician events.
400 PHONE_INVALIDNo country code, e.g. 6135550177.Send E.164: +16135550177.
400 JOB_REQUEST_ADDRESS_REQUIREDNo address with a city or postal code — often customer_id for a customer without a stored address.Send customer + address with city or postal_code.
400 JOB_REQUEST_INVALID_INPUT about the time (data.means_locally)scheduled_at has a timezone offset, or is in the past.Send the business’s local clock with no offset, in the future.
400 JOB_REQUEST_QUOTE_INVALID, job_type_has_no_default_durationThe job type has no default duration.Set one in Settings → Job Types, send job_duration_minutes, or omit job_type_id.
confirmed: false with a refusalSaved, but no technician can take it then.Do not resend. Pick it up in the dashboard queue.
422 IDEMPOTENCY_KEY_REUSESame key, different body — including a corrected request resent after an error, because Crisphive stores the first answer (even a refusal) under its key.Reuse a key only to resend the exact same request after a timeout or no answer. If you change anything in the request, use a new key (for example add -2 to it).
409 IDEMPOTENCY_IN_PROGRESSThe first attempt is still running.Wait a second and retry with the same key.
429 TOO_MANY_REQUESTSMore than 240 requests per minute on one key.Wait Retry-After seconds.
Webhook status is pending_verificationThe ping did not get a 2xx (URL wrong, workflow deleted or paused).Delete it (DELETE /v1/webhooks/{id}), fix the URL, subscribe again.
409 WEBHOOK_LIMIT_REACHED25 active webhooks already exist in this environment.Delete stale ones under Settings → Developer → Webhooks.
Every event exits with “Invalid Crisphive-Signature”Wrong secret stored, or the body is not the raw bytes.Store the whsec_ value from the subscribe answer; make the trigger pass the raw body.
Events stopped arrivingThe secret expired, the key that subscribed was revoked, or 5 deliveries in a row failed (the endpoint is disabled).Check the endpoint under Settings → Developer → Webhooks; subscribe again with a valid key.

Go live

1
Create a live key

In the left sidebar, click Live in the Rehearsal | Live switch and confirm with Switch, then repeat Step 1: the new key starts with chsk_live_. Choose a lifetime up to 365 days and put the expiry date in your calendar; the Owner and Administrators are also emailed 7 days before.

2
Swap the variable

Set FIELD_OPS_API_KEY to the live key. Every step reading it switches at once.

3
Subscribe the live webhook

Sandbox subscriptions only receive sandbox events. Repeat the subscribe call with the live key, store the new whsec_ secret in FIELD_OPS_WEBHOOK_SECRET, and delete the sandbox subscription.

4
Know what changes

Live jobs land on your real board, real technicians are assigned, and customers receive confirmations by email (by text only with sms_opt_in: true).

5
Renew or revoke

Keys cannot be extended: create the replacement, update the variable, check a run, then click Revoke access on the old key in Settings → Developer → API Keys. Revoking stops every call and disables the webhooks that key created.

FAQ

Do I need a Crisphive app on Pipedream?
No. This guide’s HTTP and Node.js steps call the Crisphive public API directly.
Is Pipedream shutting down?
Pipedream stopped new sign-ups for Workflows on 30 September 2026 and has announced Workflows will close on 31 March 2027. Pipedream Connect and its MCP server continue. Crisphive also works with n8n and directly with AI assistants over MCP.
Will a Pipedream retry book the same job twice?
Not if you send an Idempotency-Key built from steps.trigger.context.id. A retry for the same event replays the first answer.
Why did my job not confirm?
confirmed: false means the job is saved but no technician could take it then — outside working hours, nobody covering that address or skill, or everyone busy. refusal.error_code says which. It waits in the coordinator queue; do not resend.
How do I know a webhook really came from Crisphive?
Check the Crisphive-Signature header: an HMAC-SHA256 of the raw body with your whsec_ secret, as in the verification step above. Reject anything that does not match or is older than 5 minutes.
What is the difference between a chsk_test_ and a chsk_live_ key?
chsk_test_ uses an isolated sandbox copy of your business — no real customer is contacted. chsk_live_ uses your real jobs and customers.
Who can subscribe webhooks for my business?
Someone with Developer access — by default only the Owner. The Owner and Administrators are emailed the first time a key subscribes to events.
How do I remove Pipedream’s access?
In Crisphive, open Settings → Developer → API Keys and click Revoke access on the key. Calls stop immediately and the webhooks it created are disabled. Delete the environment variables in Pipedream too.