Envie
ReferenceSDK

Private surface

envie.private.* — the full server-side API: shop state, lists, analytics, transfers, webhooks, keys, and settings.

Everything under envie.private.* needs a private-mode client; in a browser these methods throw before any request is sent.

import { createEnvie } from "@getenvie/client";

const envie = createEnvie({
	mode: "private",
	shop: "your-store.myshopify.com",
	secretKey: process.env.ENVIE_SECRET_KEY!, // Envie → Developers, shown once
});

Shop & settings

const shop = await envie.private.shop();
shop.plan; // "FREE" | "GROWTH" | "PLUS"
shop.usage; // { lists, items } — live counters
shop.settings; // guestEnabled, shareEnabled, …

const embed = await envie.private.embedStatus();
embed.status; // "enabled" | "disabled" | "unsupported" | "unknown"

await envie.private.updateSettings({ shareEnabled: false });

updateSettings is a patch — send only what you change. klaviyoKey is write-only (never returned; null disconnects).

Lists

// Browse, newest first, cursor-paginated (limit ≤ 100):
const page = await envie.private.lists({ limit: 50 });
page.data; // list summaries (no items)
page.pageInfo.nextCursor; // pass back as { cursor } — null when done

// Per customer, or one list in full:
const theirs = await envie.private.lists({ customerId: "6301397778619" });
const full = await envie.private.list("lst_cmef8lmao0001"); // items included

Analytics

const top = await envie.private.topProducts("30d"); // "7d" | "30d" | "90d"
top.data; // [{ productId, handle, titleSnapshot, saves }] — most-wishlisted first

const activity = await envie.private.activity("7d");
activity.data; // [{ date, added, removed }] — one entry per day

The same numbers the merchant's dashboard shows — build your own reporting on top.

Exports & imports

Async jobs: create → poll → download from a signed URL (valid 24 h).

const { jobId } = await envie.private.createExport("csv"); // or "json"

let job = await envie.private.export(jobId);
while (job.status === "queued" || job.status === "running") {
	await new Promise((resolve) => setTimeout(resolve, 2000));
	job = await envie.private.export(jobId);
}
job.resultUrl; // signed download link — job.error explains a "failed"

const history = await envie.private.exports(); // past jobs, paginated

Imports mirror the shape — upload target first, then the job (full flow, formats and skip rules in the import guide):

const { uploadUrl, fileUrl } = await envie.private.createUploadTarget({
	filename: "swym-export.csv",
	contentType: "text/csv",
});
await fetch(uploadUrl, { method: "PUT", body: file }); // straight to storage, 15-min window

const dryRun = await envie.private.createImport({ source: "swym", fileUrl, dryRun: true });
// review the report via import(dryRun.jobId), then re-run with dryRun: false

const status = await envie.private.import(dryRun.jobId);

Webhooks

const created = await envie.private.createWebhook({
	url: "https://example.com/hooks/envie",
	events: ["item.added", "item.removed", "list.merged", "list.shared"],
});
created.secret; // signing secret — shown once, store it now

await envie.private.testWebhook(created.id); // signed ping through the real pipeline
const all = await envie.private.webhooks();
await envie.private.deleteWebhook(created.id);

Envelope format, signature verification and retry behavior: webhooks guide.

API keys

const key = await envie.private.createKey("reporting service");
key.secret; // env_sk_… — shown once, never retrievable

const keys = await envie.private.keys(); // metadata only, never secrets
await envie.private.revokeKey(keys[0].id); // effective within a minute

One key per service: rotation stays surgical, and lastUsedAt tells you what's actually in use.

Common mistakes

  • Polling jobs or analytics in tight loops. 600 requests/minute per key; a 2-second poll interval is plenty for transfer jobs.
  • Storing createKey/createWebhook secrets "later". Both are shown exactly once. Capture them in the same breath that creates them.
  • Rebuilding the dashboard from lists() at scale. For "what changed", subscribe via webhooks; for aggregates, use the analytics endpoints — browsing every list is the slow, rate-limited way to both.

On this page