Skip to main content

Customers

An order needs no account: a visitor can check out as a guest. An account is for everything else — a shortlist that follows them, and their orders in one place.

These are the shop's own customers, not Typograph accounts. Someone buying a birth announcement never signs up for a design platform.

Make these calls from the visitor's browser or from your own server. Every call after sign-in carries the customer's token in X-Customer-Token, with the shop key as on every call. A page on a site on the shop's Allowed websites list may send that header and the PUT and DELETE of the shortlist (see What a browser may send). Keep the token for as long as the customer stays signed in: in the page, or in your storefront's session when your server makes the calls.

Endpoints​

MethodPathDescription
POST/v1/shop/customersCreate an account
POST/v1/shop/sessionsSign in
DELETE/v1/shop/sessionsSign out, on every device
GET/v1/shop/users/meThe signed-in customer
POST/v1/shop/users/me/back-office-linkA link into the back office, for staff
PUT/v1/shop/users/me/favorites/{slug}Put a product on the shortlist
DELETE/v1/shop/users/me/favorites/{slug}Take it off again
GET/v1/shop/users/me/ordersWhat this customer ordered

Create an account​

curl -X POST https://shop-api.typograph.nl/v1/shop/customers \
-H "X-Shop-Key: shop_pk_…" \
-H "Content-Type: application/json" \
-d '{"email": "emma@example.com", "password": "at least 8 characters", "name": "Emma Clarke"}'

name is optional; the password needs at least 8 characters. 201:

{
"data": {
"id": "019f7fa1-0c2d-7e3f-8a4b-5c6d7e8f9a01",
"email": "emma@example.com",
"name": "Emma Clarke",
"favorites": [],
"is_staff": false,
"token": "…"
}
}

Sign in​

curl -X POST https://shop-api.typograph.nl/v1/shop/sessions \
-H "X-Shop-Key: shop_pk_…" \
-H "Content-Type: application/json" \
-d '{"email": "emma@example.com", "password": "…"}'

200, with the same body as above. A wrong password and an unknown address get the same answer, so sign-in cannot be used to find out who shops here.

Sign-in is limited to five attempts per e-mail address in fifteen minutes, counted for known and unknown addresses alike. Over that it answers 429 too_many_requests with a Retry-After header in seconds. A successful sign-in clears the count.

The token is handed out only here and at sign-up, never on a later read. It works for 14 days; after that the calls answer 401 not_signed_in, and the customer signs in again.

Sign out​

curl -X DELETE https://shop-api.typograph.nl/v1/shop/sessions \
-H "X-Shop-Key: shop_pk_…" \
-H "X-Customer-Token: …"

204, without a body. This ends every token of the account, not only the one you sent: a token is signed rather than stored, so merely forgetting it in your storefront would leave a copy working until it expires. A call with no token, or one that no longer works, answers 401 not_signed_in.

The signed-in customer​

curl https://shop-api.typograph.nl/v1/shop/users/me \
-H "X-Shop-Key: shop_pk_…" \
-H "X-Customer-Token: …"

The account as above, without token.

  • favorites are product slugs, in the order they were added.
  • is_staff says whether this person has a role in the shop's back office. Use it to show them the way in, with the link below.

Into the back office​

The storefront and the back office are two hosts with two sessions. To take somebody who works in the back office there without asking for the password again, ask for a link right after they sign in and send the browser to it:

curl -X POST https://shop-api.typograph.nl/v1/shop/users/me/back-office-link \
-H "X-Shop-Key: shop_pk_…" \
-H "X-Customer-Token: …"
{
"data": {
"url": "https://shop-api.typograph.nl/login/link?…",
"expires_at": "YYYY-MM-DDT14:06:00+02:00"
}
}

The link signs in once and expires after a minute; a new password voids it. Only a token handed out in the last five minutes buys one, so a token that was copied or left open cannot be turned into a back-office session later. Without a link, send them to the back office anyway: it asks for the password.

The shortlist​

curl -X PUT https://shop-api.typograph.nl/v1/shop/users/me/favorites/welcome \
-H "X-Shop-Key: shop_pk_…" \
-H "X-Customer-Token: …"

PUT adds a slug and DELETE removes it. A slug the shop does not sell answers 404 unknown_product; a shortlist already holding 200 designs answers 422 too_many_favorites. Both answer with the shortlist as it now stands:

{
"data": { "favorites": ["welcome", "confetti"] }
}

Read each product with GET /v1/shop/products/{slug}. A product that is no longer on sale answers 404; leave it out.

Order history​

curl https://shop-api.typograph.nl/v1/shop/users/me/orders \
-H "X-Shop-Key: shop_pk_…" \
-H "X-Customer-Token: …"
{
"data": [
{
"number": "ST-YYYY-0042",
"access_token": "…",
"status": "new",
"total_cents": 2965,
"placed_at": "YYYY-MM-DDT14:05:00+02:00"
}
],
"meta": { "total": 1, "limit": 100, "offset": 0, "has_more": false }
}

The orders placed with this customer's token (see X-Customer-Token on placing an order), newest first, at most 100. meta.total counts every order of the account, so meta.has_more tells you some are older than the 100 in data. This list cannot be paged: open an older order with its own token, from its confirmation. They are not matched on the e-mail address, because nothing proves an address belongs to whoever registered it: an order placed as a guest never appears here, also not after the customer makes an account. Keep the order's own token to read it back. Each row carries that token, to open the order with GET /v1/shop/orders/{number}. Its lines say how the print-ready PDF of each design is doing, and hand out its download once it is ready: see The print file of a line.

Errors​

StatuserrorMeaning
422invalid_emailNot a valid e-mail address
422weak_passwordA password shorter than 8 characters
422email_takenAn account with this address already exists
422invalid_credentialsThe address and the password do not belong together
401not_signed_inNo token, or one the shop no longer accepts
403not_staffA back-office link for an account with no role in the back office
403sign_in_againA back-office link for a token older than five minutes
404unknown_productPUT of a slug the shop does not sell
422too_many_favoritesThe shortlist already holds 200 designs
429too_many_requestsFive sign-in attempts for this address in fifteen minutes; wait Retry-After seconds