Skip to content
Esc
  • OverviewGuidesWhat exists today, and where to start.
  • QuickstartGuidesKey, domain, first send — in that order.
  • AuthenticationGuidesBearer keys, the mandatory User-Agent, and what each refusal means.
  • ErrorsGuidesThe whole vocabulary, with the status each name carries.
  • IdempotencyGuidesRetry a send without sending it twice.
  • PaginationGuidesCursors are item IDs, not page numbers.
  • Rate limitsGuidesTen a second per team, and the headers that tell you where you are.
  • Sending newslettersGuidesUse a campaign, or include an unsubscribe link.
  • EventsGuidesEvery event a webhook can carry, with one real payload each.
  • DomainsGuidesThe records, where they go at each registrar, and what the page does while you wait.
  • TrackingGuidesOpens and clicks: one record, two toggles, and what an open really means.
  • Sign-up formsGuidesPop-ups and embedded forms on your site, and the rules that pick one.
  • Website trackingGuidesrasket.js on your own site: the snippet, consent, three events and the check.
  • TemplatesGuidesFifty-seven starters sorted by goal, and how to keep a campaign as a template.
  • ReceivingGuidesInbound mail, and the Inbox: a webhook fires, you read it, you answer it.
  • InboxGuidesChannels, personal mailboxes and seats: who sees what, and where a reply goes.
  • Mail appsGuidesComing soon: your Rasket address in Apple Mail, Outlook and the Gmail app.
  • Inbox APIGuidesOpen, read and answer support conversations from your app, with the same key.
  • Support inbox in your appGuidesA Contact us form that opens a conversation your team answers, in five minutes.
  • Replies to your emailsGuidesOne field on a send, and the customer's reply opens a conversation.
  • Let an agent triageGuidesAn AI agent reads, notes and drafts over MCP; a person sends.
  • Node SDKGuidesThe rasket package: typed from the API's own document, retries only what is safe.
  • Python SDKGuidesThe rasket package on PyPI: the Node client's methods, in snake_case, over httpx.
  • MCP serverGuidesConnect Claude, ChatGPT or any MCP client: your scopes, no key.
  • AI assistGuidesSubject lines, drafts and diagnosis — in the dashboard and over the API, off until you allow it.
  • AgentsGuidesLet an AI agent set Rasket up: the skill, the rules file, MCP, and the recipe they share.
  • OAuthGuidesLet another app act for a team: register, authorize with PKCE, exchange, refresh.
  • Single sign-onGuidesOIDC login for your team, a domain proved by DNS, enforcement and break-glass.
  • IntegrationsGuidesEvery integration and its status, and Rasket on Vercel, Netlify and Cloudflare.
  • SMTPGuidesSend from anything that speaks SMTP: settings, setup guides, limits and replies.
  • ZapierGuidesSend email, add contacts and react to email events from a Zap, with no code.
  • n8nGuidesThe Rasket node and trigger for n8n workflows: install, connect, every operation.
  • VercelGuidesAdd Rasket on Vercel: a Sending key in each project as RASKET_API_KEY, no copying.
  • WordPressGuidesThe Rasket plugin: every email your site sends, through Rasket, with a safe fallback.
  • NotionGuidesTurn a Notion database of people into contacts in a segment, once or every hour.
  • Migrating to RasketGuidesBring contacts, unsubscribes, lists and templates from Mailchimp, Klaviyo and more.
  • PipedreamGuidesRasket actions and instant sources for Pipedream workflows and Connect apps.
  • FramerGuidesThe Rasket plugin: sign-up forms on a Framer site, and Framer forms into contacts.
  • MakeGuidesComing soon: send email and react to email events from a Make scenario.
  • monday.comGuidesTurn board items into contacts and send a template when a board changes.
  • ClickUpGuidesReady-made Zapier and Make automations between ClickUp tasks and Rasket.
  • SlackGuidesEmail alerts in the Slack channels you choose, and /rasket for numbers and look-ups.
  • Google SheetsGuidesComing soon: turn a sheet tab of people into contacts in a segment.
  • AirtableGuidesComing soon: turn an Airtable table of people into contacts in a segment.
  • ShopifyGuidesRasket for Shopify: customers with their consent, store events and ready-made flows.
  • SMS quickstartGuidesSend your first text to a US or Canadian phone.
  • Get a numberGuidesToll-free or local numbers, and the registration carriers require.
  • Consent and STOPGuidesWho you may text, the proof you keep, and replies like STOP.
  • SMS campaignsGuidesText a segment, at a local time, priced before you send.
  • Texts in automationsGuidesThe Send SMS step, and what happens to a contact without a phone.
  • SMS webhooksGuidesThe six sms events and what each one carries.
  • SMS pricingGuidesTexts included in each plan, the price per message, and number fees.
  • EmailsAPI referenceSend, batch, retrieve, list, reschedule, cancel, attachments.
  • DomainsAPI referenceAdd a domain, publish its records, verify it.
  • API keysAPI referenceCreate, list, rename and revoke credentials.
  • WebhooksAPI referencePayloads, signature verification, retries and replay.
  • SuppressionsAPI referenceAddresses we will not send to, and why.
  • LogsAPI referenceEvery request made with this team's credentials.
  • MetricsAPI referenceDelivery, bounce, complaint and engagement counts.
  • TemplatesAPI referenceVersioned email content with typed variables, addressed by ID or alias.
  • ContactsAPI referenceYour audience: contacts, their typed properties, segments and topic choices.
  • SegmentsAPI referenceAudiences defined by a filter, by hand, or both.
  • TopicsAPI referenceWhat contacts subscribe to, and the preference page's list.
  • CampaignsAPI referenceCampaigns, at /broadcasts: one message to a segment, from draft to results.
  • AutopilotAPI referenceStore campaigns Autopilot drafts for you, each waiting for a yes or a skip.
  • ImportsAPI referenceCSV uploads: column mapping, conflicts and counts.
  • AutomationsAPI referenceWorkflows that run per contact: the graph, its versions, and every run.
  • Custom eventsAPI referenceThe names your product fires, and what starts a workflow.
  • ReceivingAPI referenceMail sent to you: the message, its attachments, its raw source.
  • OAuthAPI referenceClient registration, the token endpoint, and the grants a team has given.
  • TeamAPI referenceThe team a credential belongs to: its plan, sender identity, AI flag and members.
  • BillingAPI referencePlan, usage, invoices and add-ons, and the hosted pages where a customer pays.
  • AI helpersAPI referenceSubject lines, a first draft, and why an email did what it did.
  • InboxAPI referenceChannels, conversations, messages, notes and search in your support inbox.
  • SMSAPI referenceSend, schedule and cancel texts, numbers, test phones and SMS consent.

GuidesWebsite tracking

Website tracking with rasket.js

Send product views, cart updates and checkouts from your own website, so an abandoned-cart or browse flow can start from them. One script in your pages' head, one consent call from your cookie banner, and one line of code per event. Orders, and who each shopper is, come from your server.

Before you start

On Shopify, install the Rasket app instead: it sends these events, checkouts and orders for you, and nothing on this page applies. This page is for a site you build yourself.

  1. In Rasket, open Automations → Events and, under Website tracking, add your website: a name, and every origin the script runs on, one per line, such as https://shop.example.com.
  2. An origin is the scheme, the host and the port, with no path and no wildcard, up to ten of them. https://shop.example.com and https://www.shop.example.com are two origins. To test on your own machine, list your local address too, written the same way, with http and its port.
  3. The website gets a publishable key, rk_pub_…. It is meant for your HTML: it can only send the three events below, and only from the origins you listed.

To change the list later, send the whole new list with a full-access API key. The key and everything Rasket has seen from the site stay as they are:

PATCH /stores/{store_id}

{
  "name": "Shop",
  "allowed_origins": ["https://shop.example.com", "https://www.shop.example.com"]
}

The website's ID is in GET /stores. Adding the website a second time instead gives it a new key and starts its check from nothing.

1. Add the snippet to every page's head

Copy the snippet from the card, with your key in it, into the <head> of every page:

<script>
  window.rasket = window.rasket || function () { (rasket.q = rasket.q || []).push(arguments); };
  rasket("init", { key: "rk_pub_…", consent: "required" });
</script>
<script async src="https://www.rasket.com/rasket.js"></script>

The first line queues calls until the script loads, so rasket(…) works anywhere on the page after the snippet. Load the script from exactly the address above, with www: rasket.js sends events back to the origin it was loaded from, and from any other address they are refused.

Keep the snippet before any other rasket(…) call: its init starts the script, and only the first init on a page counts. If the page already starts rasket.js for sign-up forms, keep that one and add to it what this page asks of init.

With consent: "required", rasket.js sends nothing and writes no cookie until your banner says the visitor agreed to both analytics and marketing:

// In your banner tool's "accept" callback, once the visitor allows analytics and marketing:
rasket("consent", { analytics: true, marketing: true });

// If they withdraw later. The rsk_aid cookie is deleted and nothing more is sent:
rasket("consent", { analytics: false, marketing: false });

Until then, track and identify calls are dropped, not saved for later, and agreeing to only one of the two is a no. So call consent on every page load, before the page tracks anything: from your banner tool's callback when the visitor answers, and from the one that runs when a stored answer is read back.

If your site has no banner to wait for, say so in init and events are sent from the first page:

rasket("init", { key: "rk_pub_…", consent: "granted" });

Whether you need a banner is your decision, not Rasket's. Once events may be sent, rasket.js writes one first-party cookie, rsk_aid: a random ID for this browser, kept for a year. It holds nothing about the visitor.

3. Send events from your pages

A browser can send three events. Each needs one field; everything else is optional but makes better emails.

The three browser events
EventRequiredSend it when
product.viewedproduct_idA shopper opens a product page.
cart.updateditemsThe cart changes: an item added, removed or its quantity changed. An empty list means the cart was emptied.
checkout.startedcheckout_urlThe shopper reaches your checkout. The URL is the link a reminder email sends them back to.
rasket("track", "product.viewed", {
  product_id: "red-tee",
  title: "Red tee",
  url: "https://shop.example.com/products/red-tee",
  image_url: "https://cdn.example.com/red-tee.jpg",
  price: "25.00",
  currency: "USD",
});
rasket("track", "cart.updated", {
  value: "50.00",
  currency: "USD",
  cart_url: "https://shop.example.com/cart",
  items: [
    {
      title: "Red tee",
      variant: "Medium",
      quantity: 2,
      price: "25.00",
      product_id: "red-tee",
      url: "https://shop.example.com/products/red-tee",
      image_url: "https://cdn.example.com/red-tee.jpg",
    },
  ],
});

// The cart was emptied: still send it, with no items.
rasket("track", "cart.updated", { items: [] });
rasket("track", "checkout.started", {
  checkout_url: "https://shop.example.com/checkout/c-81f2",
  value: "50.00",
  currency: "USD",
  items: [{ title: "Red tee", quantity: 2, price: "25.00" }],
});
  • The store: prefix is optional: "cart.updated" and "store:cart.updated" are the same event.
  • A site that changes pages without reloading calls track on each route change, not once when the script loads.
  • An idempotency_key after the properties makes a resent event one event rather than two. Keys starting shopify: or wix: are reserved.
rasket("track", "checkout.started", { checkout_url: "https://shop.example.com/checkout/c-81f2" }, {
  idempotency_key: "checkout-c-81f2",
});
Every key an event may carry
KeyWhat it holds
valueThe cart, checkout or order total, as a decimal string. Needs currency.
currencyThree capital letters, like USD or EUR.
product_idYour own ID for the product: up to 255 characters, or a whole number.
titleThe product's name, up to 500 characters.
priceOne unit's price, as a decimal string.
urlThe product page.
image_urlThe product picture, on any host, such as your CDN.
checkout_urlThe checkout page.
cart_urlA link that rebuilds the cart.
cart_tokenYour own ID for the cart.
checkout_tokenYour own ID for the checkout.
checkout_idAnother ID for the checkout, if your platform has two.
order_idYour ID for the order. Orders only, from your server.
order_numberThe order number the shopper sees, such as #1042.
discount_codeA code the shopper applied.
itemsUp to 50 lines, each with title, quantity (a whole number from 1 to 10,000) and price, and optionally variant, product_id, variant_id, url and image_url.
  • Money is a decimal string that is not negative, with at most four decimals and no symbol or thousands separator: "1299.00", not "$1,299.00". A number is accepted and stored as a decimal string.
  • URLs are absolute, http or https. Every link a shopper could click, url, checkout_url, cart_url and each item's url, must be on one of your website's origins, so nobody can put another site's link in your emails. Images may live anywhere.
  • A key that is not in the table refuses the whole event, so a typo shows up on the check instead of quietly missing from your emails.
  • An event is at most 16 KB.

4. Tell Rasket who the shopper is

When a visitor who has agreed types their email, in a newsletter box or at checkout, say so:

rasket("identify", { email: "maya@example.com" });

From the browser this is only a claim: anyone can type an address. It is kept with the browser and ties it to nobody. The browser's events reach a contact, and can start a flow for them, only once your server has confirmed who it is (below), and only the events from then on. No contact is ever created by an event.

5. Orders and sign-ins from your server

An order is a fact only your server can state, so it is sent with an API key, through Send an event. Pass the browser's rsk_aid cookie as anonymous_id so the order joins the same shopper's views and cart:

curl -X POST "https://api.rasket.com/events/send" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "event": "store:order.placed",
  "email": "maya@example.com",
  "anonymous_id": "4f6c2a7e-0b1d-4c7a-9a52-1f3e8d6b2c90",
  "idempotency_key": "order-1042",
  "payload": {
    "order_id": "1042",
    "order_number": "#1042",
    "value": "50.00",
    "currency": "USD",
    "items": [
      {
        "title": "Red tee",
        "quantity": 2,
        "price": "25.00"
      }
    ]
  }
}'
  • Say who ordered with email or contact_id, with anonymous_id, or with both. An address that is not a contact yet is kept and starts nothing.
  • order_id is required. The other order events, store:order.paid, store:order.fulfilled, store:order.cancelled and store:order.refunded, take the same shape.
  • idempotency_key makes a retried request one order rather than two, and occurred_at back-dates an order by up to 90 days.
  • start_flows: false records an order without starting any flow, for importing past orders.
  • An order sent with an API key belongs to your team rather than to one website.

When someone signs in or checks out, tie their browser to them. From then on the browser's events reach that contact. Earlier events are not replayed.

curl -X POST "https://api.rasket.com/events/identify" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "anonymous_id": "4f6c2a7e-0b1d-4c7a-9a52-1f3e8d6b2c90",
  "email": "maya@example.com"
}'

Your server reads rsk_aid from the request's cookies when it runs on the same host as the pages: the cookie belongs to that exact host, so a server on another subdomain does not receive it. In the browser, ask for it and pass it along:

rasket("anonymousId", function (id) {
  // null until the visitor has agreed; otherwise the rsk_aid value.
});

6. Check your installation

Back on the Website tracking card, press Check installation. Four rows say what Rasket has seen from the site:

The installation check
RowTurns green when
Snippet installedRasket found rasket.js with this website's key in your home page, or an event from the site has already arrived. A tag manager adds the script after the page loads, so the second half is what turns the row green there.
Product viewsA product.viewed event from the site was accepted.
Cart updatesA cart.updated event from the site was accepted.
CheckoutsA checkout.started event from the site was accepted.

Each row carries a short note:

  • Found on shop.example.com: the snippet, with this website's key, is in your home page.
  • Received 2 min ago: the last accepted event of that kind.
  • None yet: nothing has arrived. Open a page that sends the event, in a browser where you accepted your own banner, and check again.
  • Rejected: …: the last event of that kind was refused, with the reason. Fix the call and send it again; the row turns green on the next accepted one.
  • Not found on shop.example.com: the snippet is not in the home page's HTML and no event has arrived yet.

A disconnected website is not checked, and its rows say Disconnected.

The card's badge reads Not verified until a row passes, Partly connected while some do, and Connected when all four do.

rasket.js never reads Rasket's reply, so a refusal makes no noise on your page. A row that stays on None yet usually means the request was turned away before Rasket accepted it as your website's. The browser then reports a CORS error for the /api/storefront/events request and hides the reply, and the cause is one of these:

Refusals that show as a CORS error
StatusCause
403The page's origin is not in the website's list, exactly: https://shop.example.com and https://www.shop.example.com are two origins.
401The key in init is mistyped, the website was disconnected, or its key was rotated. Rotate key ends the old key at once: paste the new snippet everywhere the old one is.
413The request is larger than 16 KB. Send fewer items, or shorter titles.
NoneThe script was loaded from another address than the snippet's, so its events go somewhere that does not know your website.

Once Rasket knows the website, a refusal comes back as { "ok": false, "error": "…" }, which your browser's network tab shows:

Refusals the network tab shows
StatusReplyWhat to do
422payload.product_id: store:product.viewed requires product_id.A required field is missing, or a field is not one of the keys above. The check shows it as Rejected.
422… is not on an allowed origin of this key.A url, checkout_url, cart_url or item url points at another site.
422A browser may send store:product.viewed, store:cart.updated, store:checkout.started. Send orders from your server.Orders come from your server with an API key.
422idempotency_key may not start with shopify:.The shopify: and wix: prefixes are reserved. Pick another key.
429Too many events from this visitor.More than 20 events in ten seconds from one IP address. Send an event when something changes, not on a timer.
429Too many events for this site.More than 200 events in ten seconds from the whole website.

7. Your products and store signals

Send Rasket your catalogue so emails can show your products and Autopilot can see what changed. Push up to 500 products at a time with a full-access API key, using the same product_id your rasket.js events send. Push a product again whenever it changes; one Rasket already holds with a newer updated_at is left as it is.

curl -X PUT "https://api.rasket.com/stores/$STORE_ID/products" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      {
        "product_id": "red-tee",
        "title": "Red tee",
        "url": "https://shop.example.com/products/red-tee",
        "image_url": "https://cdn.example.com/red-tee.jpg",
        "price": "19.00",
        "compare_at_price": "25.00",
        "currency": "USD",
        "available": true,
        "inventory_quantity": 4,
        "updated_at": "2026-10-25T09:12:00Z"
      }
    ]
  }'

Rasket compares each push with what it holds. Your first push is the baseline and records nothing, nor do the products first seen in the day after it, so a large first import is never a flood. After that, a change worth a campaign becomes a store signal:

Store signals
SignalWhen
product_newA product went live after your first push.
collection_newA drop: three or more new products within 48 hours. Its products are in facts.product_ids.
price_dropThe price fell 5% or more below the last price that held for a day, or a compare-at price above it appeared.
back_in_stockBuyable again after a day or more out of stock.
low_stockFive or fewer left, when you send inventory_quantity.
trendingOrdered at twice its usual pace this week, in five or more orders.
curl "https://api.rasket.com/stores/$STORE_ID/signals?status=open" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"
  • A signal's facts are what the catalogue saw: the product and its price, availability and stock before and after. Every number comes from your push.
  • One signal per product, kind and week. Pushing the same change again records nothing.
  • Open signals expire after three to fourteen days, by kind, and are deleted after 90.
  • GET /stores/{store_id}/products reads your catalogue back, oldest change first. Pass updated_since to fetch only what changed.

Locked triggers

In an automation's trigger picker, a store trigger whose event has not passed the check cannot be chosen yet, and says what it is waiting for, such as Needs cart updates, or Needs order events for an order trigger. It unlocks once Rasket has accepted that event, and stays unlocked.

  • A store connected through a platform app, such as Shopify, unlocks every store trigger: the app sends the events itself.
  • An automation that already uses a store trigger keeps running.