← Back to site

The Warp Notes webhook

Business workspaces Β· payload version 1 Β· last updated September 12, 2026

Every note your team finishes in a Warp Notes Business workspace can be sent, as it finishes, to a URL you choose: a Zapier or Make catch hook, or your own server. One request per note, carrying the summary and action items, the company, the people, the follow-ups, and who recorded it. Zapier and Make take it from there into HubSpot, Salesforce, Zoho, a Google Sheet, Slack, or anything else they connect to.

Prefer pictures? Every click of the HubSpot and Salesforce setup, on Zapier and on Make, with screenshots: the CRM sync guide.

Using HubSpot or Salesforce? Start from a template. The Zap is already built: the catch hook, a filter that keeps only new notes, lookups that find the contact by email, the company by website domain and the rep who recorded the note by their email, and then the CRM entry itself. Open the Warp Notes → HubSpot template (a Note on the contact, attached to the company, owned by the rep) or the Warp Notes → Salesforce template (a completed Task on the contact, related to the account, assigned to the rep, so it shows in the activity timeline like a logged call), click Try it (Use this template if you are not signed in), connect your CRM when it asks, click through to Review and publish. Then open step 1, copy the webhook URL from its Test tab into the app, and send a test. About five minutes, and nothing to map. On Make instead? Import the HubSpot blueprint or the Salesforce blueprint β€” the same lookups and the same entries, with the steps under Make.

What "on the right contact" depends on. Both templates match by exact values, never by guessing: the person's email in Warp Notes must equal the CRM contact's email, the company's website domain must equal the CRM company's domain (Salesforce also accepts an exact account name), and the rep's Warp Notes login email must be a CRM user's email. When a lookup finds nothing the entry is still written, just without that link, and it belongs to whoever connected Zapier. So the one thing that makes the promise true is emails on your people, which a CRM export and the app's Import put there in bulk. And "CRM sync ✓" on a note means your link accepted it: if the CRM later rejects it, the failure shows in Zapier's history, not in the app.

Setting it up Events The request The payload Delivery and retries Verifying the signature Zapier Make Versioning

Setting it up

  1. Get a URL that accepts an HTTPS POST. In Zapier that is a Catch Hook; in Make a Custom webhook; on your own server, any route. To just look at what arrives, a request catcher such as webhook.site gives you one instantly.
  2. In the app, open the workspace, expand Workspace settings, tap CRM sync, tap Zapier, Make or your own server, paste the URL and save. The workspace owner is the only person who can do this; every member can see that it is connected.
  3. Tap Send a test. Your receiver gets a ping within a second or two β€” and it is a dress rehearsal: it carries every block a real note carries (note, company, people, follow_ups, recorded_by), built from your most recent note and honouring the Advanced switches, so Zapier and Make learn the full field list from the test alone. A workspace with no notes yet gets clearly labelled sample values.
  4. Record a note. When it finishes, the note shows Sent to your webhook, and your receiver has a note.created.

Only new notes are sent. Connecting a workspace sends the notes recorded from then on and nothing older. To send earlier notes, the owner taps Send older notes, picks a window (30 days, 90 days, or everything), sees the count and the rough Zapier or Make cost, and confirms. Those go out about twenty a minute and arrive with "history": true. Notes that came in by CSV import are never sent.

Events

EventWhenDefault
pingThe owner taps Send a test. Same blocks as note.created, so it teaches the fields; filter it out before any CRM action.always
note.createdA note finishes processing, or the owner sends history.on
note.updatedA delivered note's title, summary, action items, company or people change.off β€” Advanced β†’ Send changes too
note.deletedA delivered note is deleted.off β€” same switch
task.createdA follow-up on a note, as its own request.off β€” Advanced β†’ Send each follow-up separately
task.updatedA delivered follow-up changes or is ticked off.off β€” both switches
task.deletedA delivered follow-up is deleted.off β€” both switches

Edits and deletions are off by default for a reason: a Zapier or Make catch hook runs its Zap on every request it receives, so an edit would create a second note in your CRM. Leave them off unless your flow handles updates. Follow-ups are always inside the note's payload; the separate task.* events exist for flows that want one request per task.

The request

An HTTPS POST with a JSON body and these headers:

HeaderValue
Content-Typeapplication/json
User-AgentWarpNotes-Webhook/1 (+https://warpnotes.com)
X-WarpNotes-EventThe event name, e.g. note.created.
X-WarpNotes-VersionThe payload version, currently 1.
X-WarpNotes-DeliveryAn id for this delivery. A retry repeats it, so you can ignore a duplicate.
X-WarpNotes-TimestampWhen the request was signed, in Unix seconds.
X-WarpNotes-Signaturesha256=<hex> β€” see verifying the signature.

Redirects are not followed. Answer any 2xx within ten seconds; anything else is a failure and is retried.

The payload

Every request starts with the same envelope, then the blocks for its event.

FieldMeaning
eventThe event name.
versionThe payload version. See versioning.
delivery_idSame as the X-WarpNotes-Delivery header.
attempt1 on the first try, higher on retries.
historytrue when the owner sent history rather than the note being new.
sent_atISO 8601, UTC.

note.created and note.updated

The full sample: sample-note.created.json. Paste it into Make's Generate from sample or use it to build your Zap's field mapping.

{
  "event": "note.created", "version": 1, "delivery_id": "…", "attempt": 1, "history": false, "sent_at": "…",
  "workspace": { "id": "…", "name": "Wasatch Supply" },
  "note": {
    "id": "…", "recorded_at": "2026-09-08T18:17:04.000Z", "source": "voice",
    "title": "Draper Ridge wants the new rooftop line",
    "headline": "Marcus asking about lead times",
    "summary": "Marcus is quoting three rooftop units … Warned him about the 8 week window.",
    "action_items": ["Confirm rooftop unit lead times with the factory", "Follow up with Marcus on the strip mall bid"],
    "transcript": "…only when the owner turns on Include the transcript…",
    "captured_location": { "lat": 40.5247, "lng": -111.8638 }
  },
  "company": {
    "id": "…", "name": "Draper Ridge HVAC", "domain": "draperridgehvac.com",
    "phone": "…", "email": "…", "website": "…", "description": null,
    "address": { "text": "1180 E 12300 S, Draper, UT 84020", "lat": 40.5247, "lng": -111.8638 },
    "territory": "South", "labels": ["Active", "HVAC"]
  },
  "people": [
    { "id": "…", "name": "Marcus Behl", "title": "Owner", "company": "Draper Ridge HVAC",
      "emails": ["marcus@draperridgehvac.com"], "phones": ["+1 801 555 0199"] }
  ],
  "primary_person": { "id": "…", "name": "Marcus Behl", "title": "Owner", "company": "Draper Ridge HVAC",
                      "email": "marcus@draperridgehvac.com", "phone": "+1 801 555 0199" },
  "follow_ups": [
    { "id": "…", "title": "Confirm rooftop lead times for Marcus",
      "due_local": "2026-09-10T11:00:00", "time_zone": "America/Denver",
      "all_day": false, "done": false, "completed_at": null }
  ],
  "recorded_by": { "user_id": "…", "name": "Jake", "email": "jake@wasatchsupply.example" }
}

task.created and task.updated

Sample: sample-task.created.json. Carries task (the same shape as an entry in follow_ups), note as { "id", "title" }, company, person (the follow-up's contact, or null) and recorded_by.

note.deleted and task.deleted

{ "event": "note.deleted", "version": 1, "delivery_id": "…", "attempt": 1, "history": false, "sent_at": "…",
  "note": { "id": "…" } }

ping

Sample: sample-ping.json. The envelope, then the same blocks as note.created β€” built from the workspace's most recent note, transcript included only when that switch is on β€” and a message. A workspace with no notes yet gets sample values, each marked "Sample". Because it is shaped like a real note, always filter on event before a CRM action, or a test would create a record.

Delivery and retries

Verifying the signature

Zapier and Make do not check signatures and do not need to. If your own server receives the requests, you can confirm each one came from Warp Notes and was not altered or replayed. When the owner saves the link, the app shows a signing key once, under Advanced; keep it on your server. Every request is signed as:

X-WarpNotes-Signature: sha256=HMAC_SHA256( secret, timestamp + "." + raw_body )

Compute the same value over the raw request body (before any JSON parsing) and compare in constant time. Reject requests whose timestamp is more than a few minutes old to rule out replays.

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

export function verify(rawBody, headers, secret) {
  const ts = headers["x-warpnotes-timestamp"];
  const given = headers["x-warpnotes-signature"] ?? "";
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;   // 5 minutes
  const expected = "sha256=" + createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
# Python
import hmac, hashlib, time

def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
    ts = headers.get("X-WarpNotes-Timestamp", "")
    given = headers.get("X-WarpNotes-Signature", "")
    if abs(time.time() - float(ts or 0)) > 300:
        return False
    expected = "sha256=" + hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(given, expected)

The owner can rotate the secret from the app at any time; the old one stops working the moment the new one is minted.

Zapier, step by step

Zapier's webhook trigger is a premium app, so it needs a paid Zapier plan. A CRM Zap is about three tasks per note β€” find the contact, create the note, create the task β€” which is worth knowing when you pick a plan.

The short way, for HubSpot or Salesforce: open the HubSpot template or the Salesforce template. Each arrives with steps 1, 4 and 5 below already built and the sample already loaded β€” the catch hook, the filter, the lookups and the CRM entry β€” so you connect the CRM, publish, then do step 2 with the URL from step 1's Test tab. Anything else you want in the entry is one edit to its body field.

  1. Create a Zap. For the trigger choose Webhooks by Zapier, event Catch Hook. Leave Pick off a child key empty. Copy the webhook URL Zapier shows.
  2. In the app: Workspace settings β†’ CRM sync β†’ Zapier, Make or your own server β†’ paste the URL β†’ Save. Then tap Send a test.
  3. Back in Zapier, click Test trigger. It finds the ping, which already carries every field a real note does, so you can map from it directly.
  4. Add a Filter by Zapier step: continue only if Event exactly matches note.created. Without it, the Zap also runs on pings.
  5. Add your actions. For HubSpot, what the template does: Find Company by company.domain and Get Owner by Email with recorded_by.email, both set to succeed when nothing is found; then Create Engagement of type Note with the title, note.summary and the action items as the body, the contact emails (people[].emails[], HubSpot accepts the whole list) as the contact association, the found company's ID as the company association, the found owner's ID as Assigned To, and halt on association error off. For Salesforce, what the template does: Find Record Contact by Email = primary_person.email, Find Record Account by Website = company.website OR Name = company.name, Find Record User by Email = recorded_by.email (all three set to succeed when nothing is found); then Create Record of type Task: note.title as Subject, the summary and action items as Description, Status Completed, note.recorded_at as Due Date, the contact's ID as Name, the account's ID as Related To and the user's ID as Assigned To. For a spreadsheet: Create Spreadsheet Row with the fields you want. Map note.recorded_at to any date column, not the time the Zap ran.
  6. Turn the Zap on. From now on every finished note in that workspace runs it.

Zapier shows arrays such as people and follow_ups as numbered fields (people 1 name, people 2 name) and as comma-separated line items. To create one CRM task per follow-up, use a Looping by Zapier step over follow_ups, or turn on Advanced β†’ Send each follow-up separately in the app and give task.created its own Zap.

Make, step by step

Make's free plan can receive the webhook and run a scenario. The blueprints below cost about nine credits per note for HubSpot and ten for Salesforce, one per module; a scenario you build yourself is usually four or five.

The short way, for HubSpot or Salesforce: download the HubSpot blueprint (a Note on the contact, attached to the company, owned by the rep) or the Salesforce blueprint (a completed Task on the contact, related to the account, assigned to the rep, so it shows in the activity timeline like a logged call). Both find the contact by email, the company by website domain and the rep by their email, and a miss leaves that link empty rather than guessing. Both were run against real HubSpot and Salesforce accounts before they were published.

  1. In Make, click Create a scenario, open the ⋮ menu at the top right, choose Import blueprint and pick the file. Every module and every mapping arrives already set.
  2. Double-click the first module, Warp Notes webhook, click Create a webhook, save it and copy the address. In the app: Workspace settings β†’ CRM sync β†’ Zapier, Make or your own server β†’ paste the address β†’ Save. Tap Send a test; the scenario ignores tests on purpose, so the app saying it was delivered is enough.
  3. HubSpot: double-click the second module and click Add next to Connection. Switch on Advanced settings and add five Custom scopes: CRM Companies Read, CRM Companies Write, CRM Contacts Read, CRM Contacts Write and CRM Owners Read. A Make HubSpot connection made with the defaults has none of them, and the modules then fail with "hasn't been granted all required scopes". If HubSpot answers the sign-in with "this app hasn't been approved to access the required scopes", a HubSpot super admin allows them once: in HubSpot, Settings β†’ Integrations β†’ Connected apps β†’ Approved apps β†’ Make β†’ Manage access β†’ Data Permissions, switch on companies read, companies write, contacts read and owners read, Save changes, then save the Make connection again. Open each of the other HubSpot modules, pick the new connection, click Save.
  4. Salesforce: double-click the second module, click Add next to Connection, keep the type Salesforce, sign in and save. Open each of the other Salesforce modules, pick the connection, click Save. Salesforce needs API access on your edition (Professional sells it as an add-on).
  5. Save the scenario. Under scheduling choose Immediately (an import sets it to every 15 minutes), then switch the scenario on. Record a note; it reaches the CRM within a minute.

Building your own instead: add the Webhooks β†’ Custom webhook module, click Add, name it, save, and paste its address into the app as above. Make shows Determine data structure: tap Send a test in the app β€” the test carries every field a real note does β€” or click Generate and paste sample-note.created.json. Add a Filter after the webhook, event equal to note.created, then your modules; Make handles arrays natively, so an Iterator on follow_ups creates one CRM task per follow-up. Two things the blueprints learned the hard way: a Make search module that finds nothing ends the route, which is why they use each app's Make an API Call module (it always answers exactly once), and Make does not escape text you map into a JSON body, which is why they pass the note through JSON β†’ Transform to JSON first. Set the schedule to Immediately and turn the scenario on.

Versioning

The version field and the X-WarpNotes-Version header are 1. New fields may be added to any block at any time without changing the version; build your mapping on the fields you use and ignore the rest. The version changes only if an existing field is renamed, removed, or changes meaning, and that will be announced here first with both versions available for a transition period.

Privacy

The workspace owner chooses the destination, can disconnect it at any time, and every member can see that it is connected. What your receiving service does with the data is governed by that service's terms. The details are in the privacy policy under Sending notes to a service you choose.

Questions, or a destination you would like supported natively: Jacob.Metcalf@appomatix.com.