How to call our APIs

Authentication

Anonymous calls

Some of our functions can be invoked without an authentication token or with an empty one. We call these anonymous calls.

  • All read-only Loyalty API functions can be called anonymously, and will assume empty customer state.
  • All Blocks API blocks can be embedded anonymously, and will similarly assume an empty customer state.

JWT tokens

Bubblehouse authenticates uses via HS256 JWT tokens that you generate on your end, based on a shared secret set up in Bubblehouse API console.

If you have never used JWT, it’s just a Base64-encoded JSON signed (in case of HS256 tokens) using HMAC-SHA256, with the shared secret as the HMAC key. API console will link you to JWT.io debugger, which allows to inspect, verify and create these tokens.

So, to reiterate:

  • Shared secrets are generated by Bubblehouse upon your request.

    You can have multiple shared secrets, perhaps for multiple environments or for secret rotation. A specific secret is selected based on Key ID field (kid) in JWT header

    Shared secrets do not expire. You can revoke them manually.

  • The actual tokens come from you. You can generate any number of tokens you want, with any supported JWT features.

Every JWT token presented to Bubblehouse needs to have:

  • kid (Key ID) field in the header specifying the particular shared secret used.
  • aud (Audience) set to BH.
  • sub (Subject) set to either a shop slug (my-shop), or a combination of shop slug and customer ID (my-shop/1234)

Additionally, you can set:

  • iat (Issued At) to have Bubblehouse reject a key before a certain time
  • exp (Expires At) to have Bubblehouse reject a key after a certain time
  • l (Level) to select admin-level or customer-level access for a given customer token
  • ce (Customer Email) to provide the customer’s email address for identity resolution

NOTE: if you do NOT include Expires At property, your tokens will never expire, and will remain valid until you revoke the corresponding shared secret. We recommend adding a reasonable expiration time and generate fresh tokens whenever needed.

Customer identity claims

For customer tokens, you can optionally include the ce (Customer Email) claim to help Bubblehouse identify customers more accurately:

  • If you include both a customer ID in the sub claim and an email in the ce claim, Bubblehouse can match customers by either identifier and keep both pieces of information synchronized.

  • This is particularly useful when you have partial customer data - for example, if you only know the customer’s email initially but not their ID in your system, or vice versa.

  • Bubblehouse will automatically update customer records to include whichever identifier was missing, making future lookups more reliable.

3 kinds of tokens

Bubblehouse supports three kinds of tokens:

  1. Shop tokens are for your backend servers, and allow acting on behalf of the shop owner.

    They MUST be kept private and cannot be exposed to your front-end code or mobile apps.

    We recommend giving these tokens a short expiration time (a few minutes), and generating a fresh one on every use.

  2. Customer tokens are for your frontend code and mobile apps, and allow acting on behalf of a single shop customer.

    When calling our RPC-style APIs, in most cases you should give these tokens a short expiration time (1 to 5 minutes is recommended).

    When using the blocks API (i.e. inserting the IFRAMEs), we recommend using a long expiration time, e.g. 7 days usually works great. This is because the customer might keep the page open for multiple days, and once the token expires, they will not be able to execute any loyalty actions.

  3. Admin customer tokens are a blend of the two above. These are for your backend servers and, perhaps, for staff-only frontend code. These allow to act on a given shop customer with full shop-level priviledges.

    These are generated similar to customer tokens, but annotated to give shop-level admin priviledges.

    Similar to normal customer tokens, use a short expiration time for normal API calls, and use longer expiration time if the token is served to your frontend staff portal and used from there.

See the example tokens on the API console for the exact claims included in each kind.

There is no reason to cache these tokens; generate a new token each time you want one. (E.g. before each API call and on every loyalty page load.) It is very cheap computation-wise.

Tokens cannot be generated in front-end JavaScript

There is _absolutely no way_ to safely generate tokens in front-end JavaScript code. If you attempt to do this, you will allow _any_ visitor of your store to make _any_ changes to the entire store and to impersonate _any_ customer or _the administrator_ of your store.

There are a few more, niche kinds of tokens that most clients won’t deal with:

  • Partner tokens (which come in partner shop and partner customer varieties) are similar to normal shop and customer tokens, but allow an authorized partner like a marketing or subscription service to act on behalf of any connected shop, as long as the shop has explicitly authorized the connection to that partner.

  • Magic link tokens are used for magic link logins from emails and similar scenarios, and are not JWT tokens. We automatically provide these to your email/SMS/CDP integrations, so you never want (nor can you) generate them yourself.

  • SSO aka Bubblepass tokens are used by our partners that provide customer authentication services to share authentication with Bubblehouse. (FWIW, that’s similar to how Shopify Multipass works.)

  • Integration tokens are used to authenticate webhooks and callbacks sent to us by some supported integrations.

  • User tokens are used to authenticate a staff user in specific single-signon (SSO) scenarios.

Authenticating RPC API calls

RPC API calls accept the token via Authorization: Bearer <token> header.

Alternatively, you can pass the token via auth query param, but this is mainly intended for debugging purposes, mostly for making trivial GET calls by opening a URL in a web browser. You SHOULD NOT use this in production; Bubblehouse reserves the right to apply development rate limiting to such tokens.

Authenticating Blocks API calls

Blocks API calls accept the token via auth query param.

Generating tokens when using Shopify Hydrogen

As described above, you need to generate JWT tokens to access Bubblehouse APIs, and that needs to happen server-side.

Tokens cannot be generated in front-end JavaScript

It is worth repeating that _there is no possible way_ to generate Bubblehouse tokens on the front-end side without compomising the security of the entire shop.

After authenticating a customer via Customer Account API, be sure to store their Shopify customer ID in your session data; you will need the customer ID to create Bubblehouse tokens. Then produce the token based on this customer ID, combined with the key ID, shared secret and shop slug from Bubblehouse API console. (See the example below.)

Example (Node.js)

Here’s an example JavaScript function that generates valid Bubblehouse tokens:

function signBubblehouseToken(subject, keyId, keySecretInBase64, validityInSeconds) {
  let nowUnix = Math.floor(Date.now() / 1000)
  let header = {'typ': 'JWT', 'alg': 'HS256', 'kid': keyId}
  let payload = {'aud': 'BH', 'sub': subject, 'exp': nowUnix + validityInSeconds}
  let raw = Buffer.from(JSON.stringify(header)).toString('base64url') + '.' + Buffer.from(JSON.stringify(payload)).toString('base64url')
  let hmac = require('crypto').createHmac('sha256', Buffer.from(keySecretInBase64, 'base64'))
  hmac.update(raw)
  return raw + '.' + hmac.digest('base64url')
}

Customer token:

signBubblehouseToken('shopify-yourshop/123456789', '1b356cdffb010001', 'Idkau7WmFTRh1w+pX0Snp3EQA6pJ+h19qdb38ylwIyY', 3600)

Shop token:

signBubblehouseToken('shopify-yourshop', '1b356cdffb010001', 'Idkau7WmFTRh1w+pX0Snp3EQA6pJ+h19qdb38ylwIyY', 3600*24)

Example (PHP)

Here’s an example PHP function that generates valid Bubblehouse tokens (using only standard PHP library with no dependencies):

<?php

function bubblehouse_token_sign($subject, $keyId, $keySecretInBase64, $validity_sec) {
    $raw = bubblehouse_base64url_encode(json_encode(['typ' => 'JWT', 'alg' => 'HS256', 'kid' => $keyId])) . '.' .
        bubblehouse_base64url_encode(json_encode(['aud' => 'BH', 'sub' => $subject, 'exp' => time() + $validity_sec]));
    return $raw . '.' . bubblehouse_base64url_encode(hash_hmac('sha256', $raw, base64_decode($keySecretInBase64), true));
}

function bubblehouse_base64url_encode($string) {
    return str_replace(['+','/','='], ['-','_',''], base64_encode($string));
}

Customer token:

echo bubblehouse_token_sign('shopify-yourshop/123456789', '1b356cdffb010001', 'Idkau7WmFTRh1w+pX0Snp3EQA6pJ+h19qdb38ylwIyY', 3600)

Shop token:

echo bubblehouse_token_sign('shopify-yourshop', '1b356cdffb010001', 'Idkau7WmFTRh1w+pX0Snp3EQA6pJ+h19qdb38ylwIyY', 3600*24)

Final word: Tokens cannot be generated in front-end JavaScript

You cannot safely generate tokens in frontend JavaScript code. It needs to happen server-side.

There is no possible way whatsoever to do that without allowing any visitor of your store to make any changes to the entire store and to impersonate any customer or any administrator.

Previous
Versioning