Skip to main content
@shoplygg/node is the API for a store’s own server: every route of this reference is a method, with TypeScript types, retries that are safe to repeat, lists a page at a time or all at once, and a check that a webhook came from Shoply. Each route’s page shows its call.
Node 20 or later. It has no dependencies.
Make the key under Developers, API Keys in the dashboard, with the permissions your code needs (Authentication). Left out, apiKey is read from SHOPLY_API_KEY. A key is a secret: the client refuses to start in a web page. For a buyer’s browser, use the Customer API below.

A route is a method

A route’s method is its name in this reference, and its page shows the call. Get an order is orders.show, so shoply.orders.show(orderId). A call takes:
  1. The IDs in the route’s address, in order. The store’s own is filled in for you.
  2. Its fields, as one object: what goes in the address and what goes in the body are sorted out for you.
  3. Options, last: idempotencyKey, timeout, maxRetries, signal, headers.
A call answers the route’s data: the order, the coupon, the list. The store is asked of the key once, the first time it is needed (GET /key). Pass storeId to skip that.

Lists

A list that is paged answers its first page, which for await walks row by row through every page.
A page has data, its rows, page, pages, total, and meta, whatever else the list answers beside its rows (counts, figures). A list that is not paged, such as List products, answers its rows.

Errors

A refusal is a ShoplyError.

Trying again

Every change is sent with an Idempotency-Key, a new one for each call and the same on each of its tries. So a try whose answer was lost is answered as the first one was, never carried out twice. To make a change once however many times your own code runs it, pass your own. Options come after the fields, so a route whose fields you leave out takes {} before them:
A read, or a change with a key, is tried again up to twice more when nothing answered, or the answer was 408, 429, 500, 502, 503 or 504, or 409 request_in_progress. It waits as long as Retry-After says, or half a second, then a second, then two, a little more or less. Told to wait more than a minute, it does not, and throws with retryAfter. An upload is not tried again, since it carries no key. Pass { maxRetries: 0 } to a call, or to the client, to try once. Pass { signal } to stop a call.

Files

A route that answers a file, a CSV or a PDF, answers the Response itself:
An upload takes file as a Blob, a File, a Buffer or a Uint8Array:
What else the route takes goes beside file: a dispute’s file says what it is.

A route newer than the package

request() reaches any route by its method and its address after /v1, with {storeId} filled in. It answers the whole envelope (Conventions).

Webhooks

webhooks.verify checks a delivery’s signature with your endpoint’s signing secret, refuses one signed more than five minutes ago, and answers the event. Pass the raw body, as it arrived: a body parsed and written out again is not what was signed. See Webhooks.
With TypeScript, switch (event.event) narrows event.data to what that event sends. webhooks.sign(body, secret) makes the headers Shoply would send, to test your handler with.

The Customer API

ShoplyCustomer is the Customer API: a buyer signs in with a code emailed to them, and reads their own orders, keys, subscriptions and affiliate page at your store. It holds the buyer’s token and no key, so it runs in a browser or an app as well as on a server.
To keep a buyer signed in, keep buyer.token where only your page or app can read it, and pass it back as token next time. A change made with a buyer’s token is never sent again by itself.

OAuth

An app that sellers connect to their own stores (see Apps and OAuth) passes the token it is handed as apiKey, and oauth has the steps that get one: oauth.authorizeUrl(), oauth.state(), oauth.pkce(), oauth.exchange() and oauth.revoke().
A refusal from the token address is a ShoplyError whose code is OAuth’s word: invalid_grant, invalid_client. A notice that a seller disconnected your app is checked with webhooks.verify and your app’s notice secret.

TypeScript

The types come with the package. A route’s fields are typed as this reference describes them, with their words, so a missing field or a value a field does not take is caught as you write it. What a route answers is typed from an answer that route gave, as its page’s example is. A field that was text there may be null in another answer, and a route with no example answers unknown. Each type is named after its route: OrdersShowResult, CouponsCreateParams, OrdersListItem.