T2Shop
T2Shop documentation

Add a shop to any website in minutes.

T2Shop gives you a hosted product catalog, shopping cart, and payments — all embedded on your existing website with a single script tag. No backend required.

Quick start

Go from signup to a live cart on your website in under 15 minutes.

  1. 01

    Create an account

    Sign up at /register — takes 30 seconds, no credit card required.

    /register
  2. 02

    Create your store

    Give it a name and your website URL. You can have multiple stores later.

  3. 03

    Add products

    Go to Products → New product. Set a name, price, and stock count.

  4. 04

    Embed the cart widget

    Paste one script tag into your website. Works on any platform — Webflow, WordPress, plain HTML.

  5. 05

    Connect your bank

    Go to Payments → Connect Stripe. A quick onboarding form and you can start accepting payments.

Embed the cart

Choose how you integrate: script tag + global (no bundler) or npm package + createT2ShopWidgets (React, Next.js, Vite, etc.). The cart opens as a slide-over drawer. Logged-in merchants can copy snippets from the dashboard under Integration.

API URL & Store ID

Set apiBaseUrl to your T2Shop site origin (same host you use for the dashboard), e.g. https://your-store.t2shop.app. In the dashboard, go to Store → Settings and copy your Store ID for storeId.

html
<!-- Paste before </body> on every page. Use your hosted widget URL (see dashboard → Integration). -->
<script src="https://cdn.t2shop.dev/widget/latest/t2shop-widget.js" defer></script>
<script>
  document.addEventListener("DOMContentLoaded", () => {
    T2ShopWidgets.config({
      apiBaseUrl: "https://YOUR_T2SHOP_DOMAIN",
      storeId: "YOUR_STORE_ID",
      widgets: {
        cart: true,
        // Or pass options instead of true:
        // cart: {
        //   elementName: "t2-cart-widget",
        //   theme: { primaryColor: "#2563eb", mode: "light" },
        //   ui: { floatingPlacement: "bottom-right", floatingSize: "sm" },
        // },
      },
    });
  });
</script>

Connect it to your buttons

After the widget loads, you can call these from any button or link on your page:

js
// Cart is available only after config() with widgets.cart enabled.
// Optional: guard custom "Add to cart" buttons
if (!T2ShopWidgets.isCartWidgetEnabled()) return;

// Open / close the cart drawer
T2ShopWidgets.cart.open();
T2ShopWidgets.cart.close();

// Add a line item (image URL is required for the cart row)
await T2ShopWidgets.cart.addProduct({
  productId: "YOUR_PRODUCT_ID",
  image: "https://your-cdn.com/product.jpg",
  quantity: 1,
});

Product IDs are shown on each product in your dashboard. Use T2ShopWidgets.config once per page load; enable the cart with widgets: { cart: true }.

Static pages: inline name and price

If you hand-write a few product cards in HTML, you can pass name and price so the cart doesn't need an extra API call to show the line. Stock and checkout still use your T2Shop store.

js
// Small static sites: pass name + price yourself (no separate catalog API call for the line label)
await T2ShopWidgets.cart.addProduct({
  productId: "YOUR_PRODUCT_ID",
  name: "Handmade mug",
  price: 24.99,
  image: "https://your-cdn.com/mug.jpg",
  quantity: 1,
});

React, Next.js, or a bundler (npm)

Install t2-shop-widgets and call createT2ShopWidgets once per app. That gives you .products.get, .cart.addProduct, and an isolated HTTP client — not the global window.T2ShopWidgets.

Recommended: singleton module

Create one file (e.g. lib/t2shop-widget.ts) that exports a single widgetApi. Import it from your components instead of calling createT2ShopWidgets in every file. The reference implementation in the monorepo is t2shop-widget/app/widget-init.ts.

ts
// Recommended: one module that creates a single SDK instance (singleton) and
// re-exports it. Import that module once at app startup (e.g. main.tsx / root layout client).
// Same pattern as the open-source demo: t2shop-widget/app/widget-init.ts

import { createT2ShopWidgets } from "t2-shop-widgets";

export const widgetApi = createT2ShopWidgets({
  apiBaseUrl: "https://YOUR_T2SHOP_DOMAIN",
  storeId: "YOUR_STORE_ID",
  widgets: {
    cart: {
      theme: { primaryColor: "#2563eb", mode: "light" },
      ui: { floatingPlacement: "bottom-right", floatingSize: "sm" },
    },
  },
});

// Elsewhere in your app:
// import { widgetApi } from "@/lib/t2shop-widget";
// await widgetApi.products.get({ pageSize: 50 });
// await widgetApi.cart.addProduct({ productId, image, quantity: 1 });

One-off inline

If you only need the cart in one place, you can construct the instance inline:

js
// React, Next.js, Vite — install "t2-shop-widgets" and call createT2ShopWidgets once:
import { createT2ShopWidgets } from "t2-shop-widgets";

const shop = createT2ShopWidgets({
  apiBaseUrl: "https://YOUR_T2SHOP_DOMAIN",
  storeId: "YOUR_STORE_ID",
  widgets: { cart: true },
});

await shop.cart.addProduct({
  productId: "YOUR_PRODUCT_ID",
  image: "https://your-cdn.com/product.jpg",
  quantity: 1,
});

Load products from the API (SDK)

If you build a custom grid or search (not only static HTML cards), the SDK can fetch your store catalog from the same public endpoint as the cart: GET /api/public/v1/products. Call .products.get after config or on a createT2ShopWidgets instance.

Query parameters

  • page — 1-based page number (omit for server default, typically page 1).
  • pageSize — how many products per page (API allows 1–100).
  • search — optional filter (name/slug search on the server).
  • status DRAFT | ACTIVE | ARCHIVED; the SDK defaults to ACTIVE if omitted.

Response: items (this page), total (matching rows across all pages), page, pageSize — use total with pageSize to build pagination UI (Math.ceil(total / pageSize) pages).

Global embed

js
// After T2ShopWidgets.config(...) — wraps GET /api/public/v1/products
// page = 1-based page index; pageSize = 1–100 per request; search = filter by name/slug
const { items, total, page, pageSize } = await T2ShopWidgets.products.get({
  status: "ACTIVE",
  page: 1,
  pageSize: 20,
  search: "mug",
});

npm / createT2ShopWidgets (singleton)

Example hook pattern: t2shop-widget/app/hooks/use-store-catalog.tsx

ts
// With createT2ShopWidgets → widgetApi (widgets/products/sdk.ts)
const { items, total, page, pageSize } = await widgetApi.products.get({
  status: "ACTIVE",
  page: 2,
  pageSize: 20,
  search: "desk",
});

Pagination & search

Increase page for “next page”; combine with search for filtered lists.

ts
// Pagination: use response.total and pageSize to compute pages (e.g. Math.ceil(total / pageSize))
const pageSize = 20;
let page = 1;
const { items, total, page: currentPage } = await widgetApi.products.get({
  status: "ACTIVE",
  page,
  pageSize,
});
const totalPages = Math.max(1, Math.ceil(total / pageSize));
// Next page: await widgetApi.products.get({ ...params, page: currentPage + 1, pageSize });

Managing products

Creating a product

Go to Products → New product. Fill in the name, price, and stock count. Set the status to Active when you're ready for it to appear in your cart.

Product status

Draft products are hidden from your cart. Active products appear immediately. Archive a product to hide it without deleting it.

Stock tracking

T2Shop tracks stock automatically. When stock reaches 0, the product is no longer purchasable in the cart.

Accepting payments

T2Shop uses Stripe to process payments. Each store connects its own Stripe account — money goes directly to your bank without passing through T2Shop.

1. Connect Stripe

From your dashboard, go to Payments and click Connect Stripe. You'll be walked through a short Stripe onboarding — takes about 5 minutes.

2. Enter bank details

Stripe will ask for your bank account information so payouts can be deposited directly. This is handled entirely by Stripe — T2Shop never sees your bank details.

3. Start accepting payments

Once onboarding is complete, your cart widget will accept card payments. Payouts are deposited to your bank on a rolling 2-day schedule.

Test mode

While testing, use the Stripe test card 4242 4242 4242 4242 with any future expiry and any 3-digit CVC.

Allowed domains

For security, your cart widget only works on domains you explicitly allow. This prevents anyone else from embedding your store on their website.

How to add your domain

  1. 1. In the dashboard, go to Store → Security
  2. 2. Under Trusted Origins, enter your website URL
  3. 3. Save — the cart will work on that domain immediately

Cart not loading?

If the cart silently fails, check that your website's exact domain (including https://) is listed in Trusted Origins. Both www and non-www versions must be added separately.

Viewing orders

Every checkout from your cart widget creates an order in your dashboard.

Order statuses

Pending → Requires payment → Paid → Fulfilled. You can see the full status timeline on each order's detail page.

Customer details

Each order shows the customer's name, email, and what they ordered. Use this to fulfil physical goods or trigger your own notifications.

Payment records

The payment section of each order shows the Stripe payment ID and amount. Use this for refund disputes or reconciliation.

Go-live checklist

Run through this before accepting your first real payment.

  • Account created and verifiedDone
  • Store created with a name and site URLDone
  • At least one product set to ActiveDone
  • Widget script added to your websiteDone
  • Your website domain added under Store → Security → Trusted Origins
  • Stripe account connected from Payments → Connect Stripe
  • Test checkout from your live website