> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shoply.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer API

> Let buyers sign in on your own site or app, and show them their orders, keys, subscriptions and affiliate page

Every store has a customer panel on its myshoply.gg address. The Customer API is the same thing for your own site, or for an app: a buyer signs in with a code sent to their email, and gets a token that reaches their own orders at your store and nothing else.

There is no API key in it. The buyer's own browser or app asks Shoply, and Shoply sends the code and decides who they are.

## The Customer API or an API key

| | |
| - | - |
| **The Customer API** | The buyer's device asks Shoply. Shoply emails the code and checks it. Nothing of yours has to run on a server. |
| **An API key** | Your server asks, for a buyer your own sign-in proved. Your sign-in can be anything: a password, Discord. See [A customer or affiliate panel of your own](/guides/your-own-panel.md). |

## Signing a buyer in

1. The buyer types their email address. [Send a sign-in code](/api-reference/customer-api/send-a-sign-in-code.md) emails them six digits, in your store's name.
2. They type the code. [Sign in with the code](/api-reference/customer-api/sign-in-with-the-code.md) answers their token.
3. Every other request sends it: `Authorization: Bearer shoply_ct_...`.

```js theme={null}
const api = "https://api.shoply.gg/v1/stores/YOUR_STORE_ID/customer";
const json = { "Content-Type": "application/json" };

// 1. The code, to the address they typed.
await fetch(`${api}/login`, { method: "POST", headers: json, body: JSON.stringify({ email }) });

// 2. The code they typed, for their token.
const signedIn = await fetch(`${api}/verify`, {
  method: "POST",
  headers: json,
  body: JSON.stringify({ email, code }),
}).then((res) => res.json());

const token = signedIn.data.token;

// 3. Their orders.
const buyer = await fetch(api, { headers: { Authorization: `Bearer ${token}` } }).then((res) => res.json());
```

Your store's ID is in **Settings**, **General**. It is not a secret: a page may show it.

A code works for 10 minutes and five tries, and signs in once.

### What the answer says about an address

The same thing for every address. A code is only emailed to an address that has orders at your store, and the answer does not say whether it did, so nobody can use your sign-in to find out who has bought from you. Say so on your page: "If you have bought here, a code is on its way."

## Keeping the token

* It works for 30 days from sign-in, then the buyer signs in again. Using it does not make it last longer.
* Keep it where only your page or app can read it: in memory, or your app's own secure storage. Never put it in an address, where it ends up in logs and in what the browser tells the next site.
* A buyer holds 20 tokens at your store at most. Signing in a 21st time ends their oldest.
* [Sign out](/api-reference/customer-api/sign-out.md) ends the token the request is made with. [Sign out everywhere](/api-reference/customer-api/sign-out-everywhere.md) ends every token they hold at your store, and signs them out of its customer panel.

## What a token reaches

| What your page shows | Ask |
| - | - |
| Their orders, what they have spent, their store balance, their downloads and setup instructions | [Get the signed-in buyer](/api-reference/customer-api/get-the-signed-in-buyer.md) |
| One order, with its license keys and files | [Get an order](/api-reference/customer-api/get-an-order.md) |
| Their affiliate link, what it earned and their payouts | [Get the affiliate page](/api-reference/customer-api/get-the-affiliate-page.md) |
| Joining, a new code for their link, a payout, commission to store balance | [Join the affiliate programme](/api-reference/customer-api/join-the-affiliate-programme.md), [Change the link's code](/api-reference/customer-api/change-the-links-code.md), [Ask for a payout](/api-reference/customer-api/ask-for-a-payout.md), [Take back a payout request](/api-reference/customer-api/take-back-a-payout-request.md), [Move commission to store balance](/api-reference/customer-api/move-commission-to-store-balance.md) |
| Their subscriptions | [List subscriptions](/api-reference/customer-api/list-subscriptions.md) |
| Cancelling, pausing and the rest | [Cancel a subscription](/api-reference/customer-api/cancel-a-subscription.md), [Undo a cancellation](/api-reference/customer-api/undo-a-cancellation.md), [Pause a subscription](/api-reference/customer-api/pause-a-subscription.md), [Resume a subscription](/api-reference/customer-api/resume-a-subscription.md), [Give another card](/api-reference/customer-api/give-another-card.md) |

These are the customer panel's own, and they answer what the panel shows:

* What your settings switch off is refused here too: an affiliate programme that is off, payouts you do not allow, cancelling or pausing that you do not let buyers do.
* An order's keys and files are left out while you hold them back: a subscription that ended or is paused, or a payment that is [disputed](/guides/disputes.md) where you take access back.
* Opening an order is recorded, as on its order page, as a look from your own site. It is part of what a [dispute](/guides/disputes.md) can show the bank.
* `productPath` on an order's line is the product's path on your store's own site.

## Limits

| What | How many |
| - | - |
| Codes asked for, from one address | 10 an hour |
| Codes sent to one buyer | One a minute, five an hour, ten a day |
| Codes your store sends | 60 an hour |
| Codes tried, from one address | 30 in fifteen minutes |
| Wrong codes for one buyer | 20 a day, then no code is checked for them until the day is over |
| Reads, with one token | 300 in ten minutes |
| Changes, with one token | 30 in ten minutes |

Ask from the buyer's own device. Asking for a code and signing in are counted for the address that asks, so a server asking for every buyer would run out at once. Every answer to a request with a token says where its budget stands, in the same headers as the [rest of the API](/api-reference/rate-limits.md).

## When it says no

| Code | What it means |
| - | - |
| `customer_token_required` | No token was sent, or an API key was sent in its place. |
| `invalid_customer_token` | The token has ended, was signed out, or is another store's. Sign the buyer in again. |
| `code_cooldown` | Too soon for another code. `resendIn` is how many seconds to wait. |
| `code_wrong` | The code is not right. |
| `code_expired` | The code is past its 10 minutes or its five tries, or was used. Ask for a new one. |
| `code_paused` | Too many wrong codes for this address today. |
| `store_busy` | Your store has sent as many codes as it may this hour, or the whole platform has. |
| `rate_limited` | Too many requests. `Retry-After` is how many seconds to wait. |

## Keeping buyers safe

* Any site may ask the Customer API from a browser, and never with cookies. A page that was not handed a buyer's token cannot do anything as them.
* The email tells the buyer to type the code only on the page where they asked for it. Ask for it there and nowhere else, never by chat or by email.
* A token is one buyer's at one store. It reaches no route that takes an API key, and an API key reaches none of these.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.