Skip to content
Documentation menu: API reference

API reference

Send pageviews, custom events, conversions and purchases to theBizness.ai from your website or your server. These are the public tracking endpoints. The dashboard's data APIs require a signed-in session and are not documented here.

Base URL

https://thebizness.ai/api

Authentication

Your domain for browsers, an ingest secret for servers

Format

JSON over POST

Endpoints

The four public endpoints. The script sends pageviews automatically, and your conversion rules are checked on the server when a pageview arrives. Custom events and conversions are sent only when your code calls window.thebizness(...) or one of these endpoints.
  • POST
    /api/events
    Record a pageview
  • POST
    /api/events/event
    Record a custom event, optionally with revenue
  • POST
    /api/conversions
    Record a conversion, optionally with an amount
  • POST
    /api/conversions/check
    Legacy: check a page URL against your conversion rules

Authentication

Every request names a website with websiteId and proves where it comes from. There are two ways, and the way decides how the data is labeled in your dashboard.

From a visitor's browser

What the tracking script does
  • The request must carry an Origin header, or a Referer header when there is no Origin, whose host is your website's domain or one of its subdomains. A leading www. is ignored.
  • Neither header: 403 origin_required. Another domain: 403 origin_not_allowed. Nothing is stored.
  • localhost and 127.0.0.1 are accepted only outside production, or when your website's own domain is localhost.
  • Revenue and conversions that arrive this way are client-reported and shown as unverified.

From your server

With the website's ingest secret
  • Send Authorization: Bearer <ingest secret>. The origin check is skipped.
  • Find the secret on the Configure page of your dashboard. You can show it there and rotate it. Rotating invalidates the old secret, within a minute on every server.
  • A Bearer token that is not the website's secret is always 401 invalid_secret, even from your own domain.
  • Revenue and events recorded this way are verified.
  • Keep the secret in an environment variable on your server. Never put it in browser code. Browsers cannot send it to these endpoints on purpose, because Authorization is not an allowed cross-origin header.

Send a sale from your server

Server-side reporting is the most reliable way to record revenue you collect outside Stripe, and it is the way to include the customer's email address with a sale. Read the visitor and session IDs from the buyer's thebizness_visitor_id and thebizness_session_id cookies, so the sale is linked to the visit that brought them. Without them the sale is still recorded, but it has no traffic source.

// Server-side only. The ingest secret belongs in an environment variable on your server.
// Never put it in browser code, a mobile app, a public repository or a front-end build.
async function reportPurchase({ orderId, amount, currency, email, visitorId, sessionId }) {
  const response = await fetch('https://thebizness.ai/api/conversions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.THEBIZNESS_INGEST_SECRET}`,
    },
    body: JSON.stringify({
      websiteId: process.env.THEBIZNESS_WEBSITE_ID,
      name: 'Purchase',
      conversionUrl: 'https://yourdomain.com/checkout/success',
      amount, // in currency units, e.g. 99.99 (not cents)
      currency, // ISO code, e.g. 'USD'
      orderId, // a repeated orderId is recorded once, so retries are safe
      visitorId, // from the buyer's thebizness_visitor_id cookie
      sessionId, // from the buyer's thebizness_session_id cookie
      metadata: { email }, // the customer on the revenue record: send it from your server, never from the browser
    }),
  });

  // 429 and 5xx mean "not stored, try again". Anything else is final.
  if (response.status === 429 || response.status >= 500) {
    throw new Error(`theBizness.ai is unavailable (${response.status}), retry later`);
  }
  const result = await response.json();
  if (result.accepted === false) {
    console.warn('theBizness.ai did not record the sale:', result.reason);
  }
  return result;
}
import os
import requests

# Server-side only: keep the ingest secret in an environment variable.
response = requests.post(
    "https://thebizness.ai/api/conversions",
    headers={"Authorization": f"Bearer {os.environ['THEBIZNESS_INGEST_SECRET']}"},
    json={
        "websiteId": os.environ["THEBIZNESS_WEBSITE_ID"],
        "name": "Purchase",
        "conversionUrl": "https://yourdomain.com/checkout/success",
        "amount": 99.99,  # in currency units, e.g. 99.99 (not cents)
        "currency": "USD",
        "orderId": "order_123456",  # a repeated orderId is recorded once, so retries are safe
        "metadata": {"email": "[email protected]"},
    },
    timeout=10,
)

# 429 and 5xx mean "not stored, try again". Anything else is final.
if response.status_code == 429 or response.status_code >= 500:
    raise RuntimeError(f"theBizness.ai is unavailable ({response.status_code}), retry later")
result = response.json()
if result.get("accepted") is False:
    print("theBizness.ai did not record the sale:", result["reason"])
curl https://thebizness.ai/api/conversions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $THEBIZNESS_INGEST_SECRET" \
  -d '{
    "websiteId": "your-website-id",
    "name": "Purchase",
    "conversionUrl": "https://yourdomain.com/checkout/success",
    "amount": 99.99,
    "currency": "USD",
    "orderId": "order_123456",
    "metadata": { "email": "[email protected]" }
  }'

Limits and plan enforcement

Request limits

  • Request bodies are capped at 32 KB. Larger requests return 413.
  • eventData and metadata objects over 4 KB are reduced, not stored whole. eventData keeps only amount, revenue, currency, orderId, email, productName and productId, so a large cart still records the sale. metadata keeps only email. Everything else is dropped, and kept strings are cut to 320 characters.
  • A revenue amount sent from a browser is at most 1,000,000 per event, in the sale's currency. The cap is scaled up for currencies whose unit is worth much less than a dollar: about 10,000,000,000 for IDR and VND, 1,000,000,000 for KRW and NGN, and 100,000,000 for JPY, INR and HUF. A larger amount is rejected with 400, and the error names the cap for that currency, such as must be at most 1,000,000 USD. A report sent with your ingest secret is only bounded by 1e12 (1,000,000,000,000) in every currency, so a large B2B sale is accepted.
  • The amount must be a plain number in major units, for example 99.99 (not cents, and not a formatted string such as "1,500.00" or "$49"). A value that cannot be read as an amount, or a negative one, still stores the event but records no sale, and the response then carries revenue: "ignored", revenueReason: "invalid_amount".
  • Text fields are trimmed and truncated. Names are capped at 100 to 200 characters, URLs at 2,048. Query strings are removed from stored URLs. Fragments are removed too, except hash-route fragments (#/path or #!/path), which are kept without their query. A hash route that contains = or & (also written %3D or %26), or that is longer than 100 characters, is dropped, and only the origin and path are stored. Sign-in redirect fragments such as #access_token=... are therefore never collected. Do not put identifiers in route paths.
  • Known bots are ignored: the pageview endpoint answers 202 bot and stores nothing.

Rate limits

Exceeding a limit returns 429 with a Retry-After header. The per-minute limits are counted per server instance.
  • Pageviews: 120 per minute for each visitor IP address and website.
  • Custom events: 60 per minute for each visitor IP address and website.
  • Revenue and conversions: 20 per minute for each visitor IP address and website.
  • Legacy conversion checks: 120 per minute for each visitor IP address and website.
  • All requests for one website: 10,000 per minute.
  • Daily allowance for browser-reported events. Metered events reported from a visitor's browser (custom events, sales, conversions and URL-rule conversions) also have a daily allowance for each IP address and website: 10% of your plan's event limit, at least 100 and at most 2,500 per UTC day (Starter 100, Pro 500, Growth 2,500), counted across all servers. A purchase event with an amount counts as two. Beyond the allowance the answer is 429 rate_limited ("Too many events from this address today") with a Retry-After to the end of the day, and nothing is counted against your plan. A conversion rule that matches a pageview over the allowance is not recorded, but the pageview is. Events you send from your server with the ingest secret, and revenue from Stripe webhooks, are never limited by it.
  • Requests for websites this server has not looked up in the last minute (unknown ids included): 120 per minute for each IP address. Known websites are served from a cache and do not count.
  • If you proxy the script through your own domain, every visitor reaches us from your proxy's address, so the per-address limits apply to your site as a whole. Requests from your own server count against the same limits, from your server's address.
  • Wrong origins, wrong secrets and unknown websites are refused before they count against the website's budget, but requests that need a lookup count toward the 120 per minute above.

Known limitation

The website ID is public, because it is in your page source. Anyone who knows it can submit browser-origin events by setting an Origin header, so the origin check is a filter and not authentication. The 120% hard cap and the daily allowance per address only raise the cost of doing that. Server-side reporting with the ingest secret is the tamper-proof path, and neither the hard cap nor the daily allowance applies to it. Use it for any count that matters, such as sales.

Plan enforcement

What happens to your data when your account is inactive or over its plan. The Terms and the pricing page describe the same rules.
  • Tracked events. Custom events and revenue or conversion events count toward your plan's limit for each billing period, across all your websites (Starter: 1,000, Pro: 5,000, Growth: 25,000). Pageviews are never counted. A visit that matches a conversion rule counts as one conversion. A purchase event that includes an amount is recorded as a custom event and as a sale, so it counts as two tracked events. An event without an amount counts as one.
  • At 100% of the limit the dashboard and billing page show a warning. Everything is still recorded.
  • Up to 120% of the limit (1,200 on Starter, 6,000 on Pro, 30,000 on Growth) nothing is dropped. Beyond it, custom events and revenue or conversions reported from a browser are refused with 202 plan_limit until the billing period renews and usage starts again at zero. A purchase event with an amount needs room for both of its events, so it can be refused when fewer than two events remain below the cap.
  • Never dropped for the limit. Revenue that Stripe reports to us is always recorded. Events you send from your server with the ingest secret are recorded without the cap while your subscription is active.
  • Inactive account. After a failed payment you keep full access for 7 days. If the subscription is then still unpaid, canceled, or missing, every endpoint answers 202 subscription_inactive and stores nothing, pageviews included. Revenue that Stripe reports to us is still recorded. Your existing data is kept, and tracking resumes when the subscription is active again.

Responses

Every request that is not stored gets an explicit JSON answer of the form { "accepted": false, "reason": "...", "error": "..." }. The tracking script logs the reason in the browser console only when you set data-debug="true".
  • 200 accepted Recorded. A repeated orderId returns duplicate: true and is not recorded again. A sale reported with a Stripe test-mode Checkout Session ID (cs_test_...) returns testMode: true: it is stored as test data and left out of your analytics.
  • 202 subscription_inactive The website owner has no active subscription (or the grace period after a failed payment has ended). Nothing was stored, pageviews included.
  • 202 plan_limit The event is above the hard cap of the plan and was not stored. It applies to custom events and revenue or conversions reported from a browser.
  • 202 bot Known bot traffic is not recorded.
  • 400 invalid_request A required field is missing or invalid, a currency is not a valid ISO 4217 code, or the body is not valid JSON.
  • 401 invalid_secret The Authorization header carries a Bearer token that is not this website's ingest secret.
  • 403 origin_required A browser request had neither an Origin nor a Referer header, and no ingest secret.
  • 403 origin_not_allowed The Origin (or Referer) is not your website's domain or one of its subdomains.
  • 404 unknown_website The websiteId does not exist. Nothing is stored.
  • 413 payload_too_large The request body is larger than 32 KB.
  • 429 rate_limited Too many requests, or, for events reported from a browser, too many from this address today. Wait for the number of seconds in the Retry-After header.
  • 500 server_error We could not record the request. Nothing was stored, and retrying is safe.

Retry only 429 and 5xx responses. A 202 or 4xx answer will not change if you send the same request again.

Endpoint details

POST /api/events

Records a pageview. The tracking script sends one on every page load and SPA navigation. The server also checks the page against your conversion rules.
POST https://thebizness.ai/api/events
Content-Type: application/json
Origin: https://yourdomain.com   (sent by the browser)

{
  "websiteId": "your-website-id",
  "url": "https://yourdomain.com/pricing",
  "visitorId": "b7c2f0e4-1d2a-4c8e-9f3b-5a6d7e8f9a0b",
  "sessionId": "3e9f1a2b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
  "referrer": "https://www.google.com/",
  "utm_source": "google",
  "utm_medium": "cpc",
  "utm_campaign": "spring_sale"
}

// 200 OK
{ "success": true, "accepted": true }

Fields

  • websiteId Required. Your website ID. An unknown ID returns 404 and nothing is stored.
  • url Required. The full http or https URL of the page.
  • visitorId Required. Persistent visitor ID (the thebizness_visitor_id cookie).
  • sessionId Required. Session ID (the thebizness_session_id cookie).
  • referrer, original_referrer Optional. Where the visitor came from.
  • utm_source, utm_medium, utm_campaign, utm_term, utm_content Optional. Campaign parameters.
  • timezone, screenWidth, performanceTiming Optional. Used for country and device reporting.

POST /api/events/event

Records a named event, such as a signup or button click. An event counts as one tracked event; an event with an amount also records a sale and counts as two.
POST https://thebizness.ai/api/events/event
Content-Type: application/json
Origin: https://yourdomain.com   (sent by the browser)

{
  "websiteId": "your-website-id",
  "eventName": "newsletter_signup",
  "url": "https://yourdomain.com/blog",
  "visitorId": "b7c2f0e4-1d2a-4c8e-9f3b-5a6d7e8f9a0b",
  "sessionId": "3e9f1a2b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
  "eventData": { "plan": "pro" }
}

// 200 OK
{ "success": true, "accepted": true }

Fields

  • websiteId Required. Your website ID.
  • eventName Required. Up to 100 characters.
  • url Required. The full http or https URL of the page.
  • visitorId, sessionId Required. Visitor and session IDs.
  • eventData Optional. A JSON object of up to 4 KB. A larger object keeps only the revenue keys listed under Request limits.
  • domain Optional. Derived from url when omitted.

Revenue events

An event creates a revenue record only when its eventData has a positive amount (or revenue when amount is missing), up to 1,000,000 from a browser (more for currencies with a small unit, see Request limits; a report with the ingest secret is only bounded by 1e12). The amount must be a plain number in major units, such as 99.99: not cents and not a formatted string such as "1,500.00" or "$49". A value that cannot be read is stored as an event without a sale, and the response says revenue: "ignored", revenueReason: "invalid_amount". The event name alone never counts, and price is ignored, so events such as add_to_cart are not recorded as sales. Currency defaults to USD. A currency you send must be a valid ISO 4217 code (in any letter case): anything else, such as ZZZ, is rejected with 400 invalid_request. orderId and email are read from eventData. From a browser the record is unverified, client-reported revenue. With the ingest secret it is verified revenue. Send the customer's email from your server, not from the browser.

A purchase event that includes an amount is recorded as a custom event and as a sale, so it counts as two tracked events. An event without an amount counts as one.

A repeated orderId for the same website is recorded once. This also holds across the two endpoints: an order sent to both POST /api/events/event (with an amount) and POST /api/conversions (with an amount) under the same orderId is recorded once. If the orderId is a Stripe Checkout Session ID (cs_...) or payment intent ID (pi_...) that a connected Stripe account has already recorded, the report is skipped. If Stripe reports the sale afterwards, the client- or server-reported record is marked as superseded and left out of your totals, and the tracked event it used is given back. A different order ID (your own order number) cannot be matched to Stripe's record, so that sale would still be counted twice.

Test mode. If the orderId is the ID of a Stripe test-mode Checkout Session (cs_test_...), the sale is treated as test data: it is stored, left out of your analytics and not counted toward your plan usage, and the accepted answer includes testMode: true. The purchase event itself is still stored and counted as one tracked event. A payment intent ID (pi_...) looks the same in test and live mode, so a sale reported with one cannot be recognized as a test and is recorded as a normal sale. This matters for the recommended thank-you page snippet, which reports the Checkout Session ID: while you test with Stripe test keys, those purchases stay out of your numbers.

If the browser already reported an order and your server then reports the same orderId with the ingest secret, the sale is upgraded to verified in place: it keeps its place in your totals and takes the server's amount, currency, email and product. The answer is duplicate: true.

POST https://thebizness.ai/api/events/event
Content-Type: application/json
Origin: https://yourdomain.com   (sent by the browser)

{
  "websiteId": "your-website-id",
  "eventName": "purchase",
  "url": "https://yourdomain.com/thank-you",
  "visitorId": "b7c2f0e4-1d2a-4c8e-9f3b-5a6d7e8f9a0b",
  "sessionId": "3e9f1a2b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
  "eventData": {
    "orderId": "order_123456",
    "amount": 99.99,
    "currency": "USD"
  }
}

// 200 OK. Recorded as unverified, client-reported revenue
{ "success": true, "accepted": true }

POST /api/conversions

Records a conversion. The script uses this endpoint for window.thebizness('conversion', …). The helper sends eventData.orderId (a string or number) as the orderId idempotency key, so pass one whenever the conversion has an amount. Server-side integrations call it directly with the ingest secret.
POST https://thebizness.ai/api/conversions
Content-Type: application/json
Authorization: Bearer <your ingest secret>   (server-side calls only)

{
  "websiteId": "your-website-id",
  "name": "Purchase",
  "conversionUrl": "https://yourdomain.com/thank-you",
  "amount": 99.99,
  "currency": "USD",
  "visitorId": "b7c2f0e4-1d2a-4c8e-9f3b-5a6d7e8f9a0b",
  "sessionId": "3e9f1a2b-4c5d-4e6f-8a7b-9c0d1e2f3a4b",
  "orderId": "order_123456",
  "metadata": { "email": "[email protected]", "plan": "pro" }
}

// 200 OK. Recorded as verified revenue
{ "success": true, "accepted": true, "id": "conv_5b1f0c7a9e2d4a6f8c3b1d0e7a9f2c4b6d8e0a1c" }

Fields

  • websiteId Required. Your website ID. An unknown ID returns 404 and nothing is stored.
  • name Required. Conversion name, up to 200 characters.
  • conversionUrl Required. The absolute http(s) URL of the page where the conversion happened. Anything else returns 400 invalid_request and is not stored.
  • amount Optional. A plain number in currency units, for example 99.99 (not cents, and not a formatted string such as "1,500.00" or "$49"). From a browser it is at most 1,000,000 (more for currencies with a small unit, such as JPY, KRW or IDR). A report with the ingest secret is only bounded by 1e12. Every conversion appears in your dashboard with its traffic source. Amounts above zero also count as revenue. A value that cannot be read records the conversion without a value and the response carries revenue: "ignored", revenueReason: "invalid_amount".
  • currency Optional. A valid ISO 4217 code, for example USD or EUR, in any letter case. Defaults to USD. Anything else, such as ZZZ, is rejected with 400 invalid_request.
  • orderId Optional. Your order or checkout ID, as a string or a number, up to 200 characters. A repeated orderId is recorded once: see below.
  • visitorId, sessionId Optional, but needed to link the conversion to traffic sources.
  • metadata Optional. A JSON object of up to 4 KB. A larger object keeps only email. The email field is shown as the customer on the revenue record. Send it from your server only.

Without orderId, every successful POST creates a new conversion, so do not retry a request that already succeeded. With orderId, a repeated request is safe: the same websiteId and orderId are recorded once, and a repeat returns { success: true, accepted: true, id, duplicate: true } without a new conversion or revenue record. The same applies when the orderId is a Stripe Checkout Session ID that the Stripe connection has already recorded for the site. A zero-amount conversion does not count as the sale: if a later report for the same orderId carries an amount, it is recorded as the sale (its own sale record, verified when you send it with the ingest secret, and timestamped when it arrives), and the earlier conversion stays a conversion. A report without an amount that follows a sale is a duplicate. If the browser already reported the order and your server repeats it with the ingest secret, the sale is upgraded to verified in place and takes the server's amount, currency, email and product (the answer is also duplicate: true).

If the orderId is the ID of a Stripe test-mode Checkout Session (cs_test_...), the conversion is treated as test data: it is stored, left out of your analytics and not counted toward your plan usage, and the accepted answer includes testMode: true. A payment intent ID (pi_...) cannot be recognized as test mode, so a conversion reported with one is recorded as normal.

POST /api/conversions/check

Legacy. Conversion rules are now evaluated on the server when a pageview arrives, so the current script no longer calls this endpoint. It still works, with its own rate limit, for copies of the script that were cached or downloaded earlier.
POST https://thebizness.ai/api/conversions/check
Content-Type: text/plain
Origin: https://yourdomain.com   (sent by the browser)

{
  "websiteId": "your-website-id",
  "url": "https://yourdomain.com/thank-you",
  "visitorId": "b7c2f0e4-1d2a-4c8e-9f3b-5a6d7e8f9a0b",
  "sessionId": "3e9f1a2b-4c5d-4e6f-8a7b-9c0d1e2f3a4b"
}

// 200 OK
{ "success": true, "accepted": true, "conversionsDetected": 1, "hasRules": true }

Fields

  • websiteId Required. Your website ID.
  • url Required. The current page URL.
  • visitorId, sessionId Optional. Used to avoid counting the same conversion twice.

Matching and de-duplication are the same as for pageviews (see Conversion rules below). The Content-Type header is optional for this endpoint, and the response only contains a count: conversionsDetected.

Conversion rules

A conversion rule says that a visit to a page is a conversion, such as reaching /thank-you. Create rules on the Configure page of your dashboard. The server compares each pageview with your rules and records a conversion for every match, so nothing extra is sent from the browser.

How a rule matches

  • Only the host and the path of the page are compared. The query string, the fragment and a trailing slash are ignored, and so is letter case.
  • Hash routes (#/page) cannot be matched; use a path-based URL for the success page, or send a custom event or the conversion API from your app. A rule whose only distinguishing part is a query string or a #/route (/#/thank-you, /?order=done) would match your home page once those are ignored, so the Configure page refuses to save it, and a rule saved earlier that looks like this is disabled instead of matching the home page. Change it to a path rule, or send the conversion explicitly with thebizness("conversion", ...) in a hash-routed app. An anchor on a real page is fine: /thank-you#done is saved as /thank-you.
  • A rule written as a path (/thank-you) matches that path on any host. A rule that starts with a host or a full URL (shop.example.com/thank-you) matches that host only. A leading www. is ignored.
  • The whole path has to match. * stands for any run of characters, slashes included. So /thank-you/* matches /thank-you/123 and /thank-you/a/b, but not /thank-you itself.
  • A rule can contain at most 3 *, and a rule that would match every page (just /*) is refused when you save it.
  • Existing rules. Rules used to match any page whose address merely contained the rule text, so checkout matched /checkout/complete. A rule now has to match the whole path. If you relied on the old behavior, change the rule on the Configure page, for example to /checkout*.
  • A rule records a given visitor (or session, without a visitor ID) at most once every 30 minutes, so reloads and back-button visits do not count again.
  • New and changed rules take effect within a minute, because each server keeps your rules for 60 seconds.
  • A rule can carry an amount and a currency. Because the visitor's browser triggers it, the Configure page applies the same cap as a browser-reported sale: 1,000,000 in a USD-class currency, more for currencies with a small unit (for example 80,000,000 JPY or 1,500,000 IDR are fine). A larger amount is refused when you save the rule, with a message such as Use an amount of 1,000,000 USD or less. For a larger sale, report it from your server with the ingest secret.
  • Each recorded conversion counts as one tracked event. A conversion triggered by a visitor's browser is client-reported.

Examples

  • /thank-you matches /thank-you, /thank-you/ and /Thank-You, not /thank-you/123.
  • /thank-you/* matches /thank-you/123 and /thank-you/a/b, not /thank-you.
  • /checkout/*/complete matches /checkout/42/complete, not /checkout/complete.
  • https://shop.example.com/thank-you matches shop.example.com/thank-you, not www.example.com/thank-you.
  • shop.example.com/orders/* matches shop.example.com/orders/9001, not example.com/orders/9001.

Proxied installs

If the script is served from your own domain, it posts conversions to /api/events/conversions on that domain. Your proxy must forward the whole /api/events/* prefix, with the Origin or Referer header of the visitor's request intact, because the origin check reads it. It should also remove the Cookie and Authorization headers before forwarding. The Next.js guide lists the proxy recipes for Next.js middleware, nginx and Cloudflare Workers.