Skip to main content

Integration Quickstart

The guide a cold developer follows to add Treatink personalization to a storefront in under a day. Everything here runs in fixtures mode — a bundled demo catalog, no backend and no API keys — which is the default. Flip to mode: 'live' when your channel is provisioned.

1. Install

The SDK is distributed as a versioned package attached to GitHub Releases.

For bundled apps: install the release tarball directly:

npm install https://github.com/treatink/treatink-sdk/releases/download/v0.1.1/treatink-sdk-0.1.1.tgz

Alternatively, download treatink-sdk-0.1.1.tgz from the releases page and install the local file:

npm install ./treatink-sdk-0.1.1.tgz

Either way the package name is unchanged:

import { Treatink } from '@treatink/sdk';

Script tag (no build step): each release also attaches the browser bundle — index.js plus its companion chunk files (chunk-*.js, designer-*.js, fixture-dataset-*.js, font-data-*.js, heic2any-*.js). index.js imports the chunks by relative path (the designer UI is lazy-loaded on first open), so host the whole set together in one directory and load only index.js, pinned with its SRI hash from the release notes:

<script
type="module"
src="/vendor/treatink-sdk/v0.1.1/index.js"
integrity="sha384-…"
crossorigin="anonymous"
></script>

Per-file SRI hashes for the chunks are also published in the release notes if you want to pin them via an import map.

The browser bundle is publishable-key only. The one secret-key operation — submitting the order — lives in a separate server entry (@treatink/sdk/server, §4) and is never importable from browser code.

2. The client flow (fixtures mode, copy-runnable)

Save this as an HTML file, serve it alongside the SDK bundle, and open it — the modal designer runs end to end with no backend.

<script type="module">
import { Treatink } from '@treatink/sdk';

// Your storefront's add-to-cart. In this fixtures demo it just records the line.
function addToCart(line) {
console.log('add to cart', line);
}

const tk = Treatink.init({
apiKey: 'pk_test_quickstart',
channel: 'petshop.example.com',
mode: 'fixtures',
});

// Open the modal designer. In fixtures mode the catalog and cutouts come from the bundled
// demo dataset (no backend, no keys) — so this snippet is copy-runnable exactly as written.
tk.designer.open({
sku: 'SSGTTBC',
cutoutLabelId: 'cut_fx_00000001', // optional: preselect a cutout (omit to let the shopper choose)
onComplete(result) {
// The shopper saved. Add the design to your cart: keep result.draftId as the line-item
// reference and result.previewUrl (a local object URL) for the cart thumbnail.
addToCart({ sku: result.sku, draftId: result.draftId, previewUrl: result.previewUrl });
},
});
</script>

What happens inside onComplete (see DesignerResult): the two artwork assets (source + rendered) have already uploaded, a reference-only draft is written to localStorage (never any image bytes), and result.previewUrl is a local object URL of the display composite. Persist result.draftId on the cart line — it is both the design's handle and the order idempotency token.

3. Build the order body (browser, pure)

At checkout, turn the saved draft(s) into the order wire body. buildPayload is pure — no network, nothing secret — and pulls variant_id, the asset ids, and the personalization block from the draft by draftId.

const payload = tk.orders.buildPayload({
externalOrderId: 'partner-1001', // your order id — also the default Idempotency-Key
displayOrderNumber: '#1001',
currency: 'USD', // the API accepts USD only
recipient: { name: 'Sam Rivera', email: '[email protected]' }, // email or phone required
destination: {
addressLine1: '1 Main St',
city: 'Austin',
region: 'TX',
postalCode: '78701',
countryCode: 'US',
},
amounts: { subtotalCents: 999, discountCents: 0, shippingCents: 295, taxCents: 0, totalCents: 1294 },
lines: [{ externalLineItemId: 'li-1', draftId, quantity: 1, unitPriceCents: 999 }],
});

The result matches the order schema in the wire contract field-for-field. Send it to your own server — the browser never holds a secret key.

4. Submit the order (server, secret key)

On your server (Node ≥ 18), submit with your secret key. Re-posting the same payload with the same Idempotency-Key (default: external_order_id) replays the original order, so retries are safe.

import { submitOrder } from '@treatink/sdk/server';

const order = await submitOrder(payload, {
secretKey: process.env.TREATINK_SECRET_KEY, // sk_test_… | sk_live_…
channel: 'petshop.example.com',
});
// order.status === 'received'; order.externalOrderId === 'partner-1001'

:::info Idempotency POST /v1/orders requires an Idempotency-Key header — submitOrder sends it automatically (default = external_order_id). Reusing the same key with a different body returns a 409 idempotency_conflict. Staging is available at https://staging.treatinkapi.com via apiBaseUrl. :::

:::note ESM-only The package ships ES modules only. If your server code is CommonJS, load it with a dynamic import — const { submitOrder } = await import('@treatink/sdk/server') — or run your server as ESM ("type": "module"). :::

5. Recommended CSP & SRI

Ship a Content-Security-Policy that allows the SDK's API/storage/image hosts. With the self-hosted script-tag install from §1, script-src 'self' already covers the bundle — pin it with the Subresource-Integrity hash published in each release's notes:

Content-Security-Policy:
script-src 'self';
connect-src 'self' treatinkapi.com <storage-host>;
img-src 'self' cdn.treatink.com blob: data:;
<script
type="module"
src="/vendor/treatink-sdk/v0.1.1/index.js"
integrity="sha384-…"
crossorigin="anonymous"
></script>

async/defer are safe; the SDK never calls document.write. Confirm the storage host with Treatink before publishing your CSP.

5.1 Privacy disclosure

Include this in your storefront's privacy notice — it reflects exactly how the SDK handles a shopper's photo:

  • Where photos go. The photo a shopper selects is uploaded only to Treatink infrastructure (the API host and its presigned storage host) over TLS, and only at save time — nothing is sent while they are still editing.
  • No third-party requests. The SDK makes no calls to analytics, trackers, external fonts, or any other origin. The only network destinations are the hosts in the CSP above.
  • No image bytes stored on the device. Drafts saved to localStorage hold references only (asset ids, layout metadata) — never the photo bytes, never a blob:/data: URL.
  • Shared-device cleanup. Call tk.drafts.clear() to remove all saved draft references (e.g. on a kiosk or shared computer). Deleting a draft never touches already-submitted orders.

6. Theming & copy

Pass theme and copy to Treatink.init — both are fully overridable (see Theming). Theme values become --tk-* CSS variables; every user-visible string has a copy key.

Treatink.init({
apiKey: 'pk_test_quickstart',
channel: 'petshop.example.com',
theme: { primary: '#8EA0F6', accent: '#EA8D00', borderRadius: '15px' },
copy: { headerTitle: 'Personalize Your Product', saveButton: 'Save Customization' },
});

7. API reference

The full, frozen TypeScript surface is documented in SDK types. Summary of what a partner touches:

7.1 Entry — Treatink.init(config)

FieldTypeNotes
apiKeystringpublishable only (pk_test_… / pk_live_…); an sk_… key throws key_scope_violation
channelstringyour registered storefront hostname
mode'live' | 'fixtures'default 'fixtures'
apiBaseUrlstring?staging override; default https://treatinkapi.com
theme / copyobjects§6
maxPersonalizationLengthnumber?text cap fallback (default 20)

7.2 Instance namespaces

NamespaceMethodPurpose
productslist(params?) / get(sku)catalog; get resolves SKU → variant
templateslist({ sku })cutout-labels for a product
artworkupload({ role, file })two-step asset upload (source/rendered) — the designer calls this for you
designeropen(options) / close()the modal designer
draftslist() / get(id) / delete(id) / clear()reference-only drafts (no image bytes)
ordersbuildPayload(input)pure order-body assembly (§3)
on(event, handler)'designer:open' | 'designer:close' | 'draft:saved' | 'error'
fixturesfailNext(op, err) / setLatency(ms)present only in mode:'fixtures'

7.3 designer.open(options)onComplete(result)

DesignerOptions: sku (required), draftId? (re-open a saved draft — restores metadata; the shopper re-selects the photo), personalizationText?, cutoutLabelId?, onComplete? / onError? / onClose?.

DesignerResult: draftId, sku, variantId?, cutoutLabelId, personalizationText?, petNamePosition?, previewUrl (local object URL), artwork { sourceAssetId, renderedAssetId }, transform, labelZone, lowRes.

7.4 Server — submitOrder(payload, options)

options: secretKey (sk_…), channel, apiBaseUrl?, idempotencyKey? (defaults to the payload's external_order_id). Returns { id, status, externalOrderId, displayOrderNumber }. Never import this from browser code.

Errors everywhere are TreatinkError { code, status?, param?, requestId? } — see Errors.