E-commerce Integration
Guide
Issue a tax document for every order in your store (WooCommerce, Shopify, a custom shop), keep customers matched to Glance clients, and never issue the same invoice twice.
Overview
A typical e-commerce integration involves three data flows:
Customers
Match each buyer to a Glance client, or create one with the order
Orders
Issue an invoice-receipt (or invoice) once per order, safely retryable
Inventory
Read stock levels from Glance by SKU
Step 0: The API Key
Create a key with action roles only, no base role: documents.create.invoiceReceipt, documents.create.invoice, documents.create.receipt, documents.create.refund, documents.view, clients.view, clients.create, products.view, inventory.view and webhooks.manage. When the store owner pastes the key, call GET /apiKeys/me to confirm it works and to show which business it belongs to (see Verifying a Key).
Step 1: Customers
The simplest path needs no separate customer sync: send the buyer inline on the document with identifyClientBy: "email". Glance matches an existing client by exact email (case-insensitive) and otherwise creates one from the client object, with its phone number and address. An existing client is never overwritten.
To look a customer up yourself, use exactEmail (or phone) on GET /clients. The plain email filter is a substring match and can return someone else:
const res = await fetch(
"https://api.glance.co.il/clients?exactEmail=" +
encodeURIComponent(order.billing.email),
{ headers: { Authorization: `Bearer ${API_KEY}` } }
).then(r => r.json());
const clientId = res.data[0]?.id; // numeric id, usable as clientIdStep 2: Order to Invoice
When an order is paid, issue an invoiceReceipt (or an invoice when payment comes later). Two things make it safe to retry:
externalId: your order id, unique per company. Sending anexternalIdthat was already used returns the document issued under it, withalreadyExists: true, instead of issuing a second one.- An
Idempotency-Keyheader: the same key on every retry of one order returns the original response. See Idempotency.
Shipping is a product line like any other, so it is taxed and shows on the document. Online payment methods have their own values: bit, paypal and wallet (Apple Pay / Google Pay), next to creditCard and bankTransfer.
await fetch("https://api.glance.co.il/documents/create/invoiceReceipt", {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}`,
},
body: JSON.stringify({
externalId: `woo-${order.id}`,
client: {
name: order.billing.name,
email: order.billing.email,
phoneNumber: order.billing.phone,
address: {
street: order.billing.address1,
city: order.billing.city,
zip: order.billing.postcode,
country: order.billing.country,
},
},
identifyClientBy: "email",
taxMode: "includeTax",
products: [
...order.items.map(item => ({
description: item.name,
sku: item.sku,
units: item.quantity,
price: item.price,
})),
{ description: "Shipping", units: 1, price: order.shippingTotal },
],
payments: [{
amount: order.total,
paymentMethod: "bit", // or "creditCard", "paypal", "wallet", ...
date: order.paidAt.slice(0, 10),
}],
sendToClient: true,
}),
});Step 3: Reconciliation
After a timeout, or when checking which orders have a document, ask by your own id: GET /documents/by-external-id/:externalId. It returns the document, or a 404 when nothing was issued under that id, which means retrying the create is safe. It needs the documents.view action role.
GET /documents/by-external-id/woo-1042 Authorization: Bearer gk_live_...
Step 4: Inventory
Read stock with GET /inventory/product/:productId, which returns availableQuantity across every warehouse, and poll it on a schedule. Webhooks (webhooks.manage) cover documents, not stock: subscribe to document.cancelled and document.refunded to keep order statuses in step when someone acts in Glance directly.
GET /products/sku/:sku to look up products by their SKU, which is typically the same across your store and Glance.Best Practices
- Send
externalIdand anIdempotency-Keyon every create rather than checking for an existing document first. The check-then-create race is exactly what issues the duplicate - Match customers with
identifyClientBy: "email"orexactEmail, never with the substringemailfilter clientIdis the client's numericid, not itsvisibleId- Use
invoiceReceiptwhen payment is already collected - Always include
skuin document products for inventory tracking - Use a key with action roles only, so a leaked store key cannot read or change anything else