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
| Method | Path | Description |
|---|---|---|
POST | /v1/shop/customers | Create an account |
POST | /v1/shop/sessions | Sign in |
DELETE | /v1/shop/sessions | Sign out, on every device |
GET | /v1/shop/users/me | The signed-in customer |
POST | /v1/shop/users/me/back-office-link | A 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/orders | What 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.
favoritesare product slugs, in the order they were added.is_staffsays 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
| Status | error | Meaning |
|---|---|---|
422 | invalid_email | Not a valid e-mail address |
422 | weak_password | A password shorter than 8 characters |
422 | email_taken | An account with this address already exists |
422 | invalid_credentials | The address and the password do not belong together |
401 | not_signed_in | No token, or one the shop no longer accepts |
403 | not_staff | A back-office link for an account with no role in the back office |
403 | sign_in_again | A back-office link for a token older than five minutes |
404 | unknown_product | PUT of a slug the shop does not sell |
422 | too_many_favorites | The shortlist already holds 200 designs |
429 | too_many_requests | Five sign-in attempts for this address in fifteen minutes; wait Retry-After seconds |