NAV
shell python ruby

Introduction

The Teezily+ API v2 enables to programmatically view the product catalog, get a single order info and complete orders to our print on demand service.

Requests are made using standard GET, PUT and POST. All responses are JSON.

Once you signed up, you will be provided with an API key.

Authentication

The API expects for the key to be included in all requests to the server in a header that looks like the following:

Authorization: Bearer [API_KEY]

If you already have a Shop API you can find its key in its settings otherwise create a new Shop API here.

Catalog Products

Get all Products and Variants

curl --location --request GET 'https://plus.teezily.com/api/v2/catalog/products' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
import http.client

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = ''
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
}
conn.request("GET", "/api/v2/catalog/products", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require "uri"
require "net/http"

url = URI("https://plus.teezily.com/api/v2/catalog/products")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Get.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"

response = http.request(request)
puts response.read_body

The above command returns JSON structured like this:

{
  "results": [
    {
      "id": 156,
      "reference": "TSRN_U",
      "name": "round neck t-shirt unisex",
      "image": "http://example.org/products/1/preview.png",
      "variants": [
        {
          "id": 135,
          "reference": "TSRN_U_DHEATHERGREY_XXL",
          "name": "round neck t-shirt unisex dark heather grey XXL",
          "color": "dark heather grey",
          "size": "XXL"
        },
        {
          "id": 134,
          "reference": "TSRN_U_DHEATHERGREY_XL",
          "name": "round neck t-shirt unisex dark heather grey XL",
          "color": "dark heather grey",
          "size": "XL"
        }
      ]
    },
    {
      "id": 285,
      "reference": "TSVN_U",
      "name": "v-neck t-shirt unisex",
      "image": "http://example.org/products/1/preview.png",
      "variants": [
        {
          "id": 165,
          "reference": "TSVN_U_WHITE_XXL",
          "name": "v-neck t-shirt unisex white XXL",
          "color": "white",
          "size": "XXL"
        },
        {
          "id": 164,
          "reference": "TSVN_U_WHITE_XL",
          "name": "v-neck t-shirt unisex white XL",
          "color": "white",
          "size": "XL"
        }
      ]
    }
  ],
  "count": 2
}

This endpoint retrieves Products and their associated Variants.

HTTP Request

GET https://plus.teezily.com/api/v2/catalog/products

Available parameters, in addition to pagination:

Parameter Required Type Description
name No String Filter by name
reference No String Filter by reference

HTTP Response

Parameter Type Description
results Array An array of Products
count Integer Number of results

Product

Parameter Type Description
id String The product id
reference String The product reference
name String The product name
image String The product image
variants Array An array of Variants

Variant

Parameter Type Description
id String The variant id
reference String The variant reference
name String The variant name
color String The variant color
size String The variant size

Orders

Create an order

curl --location --request POST 'https://plus.teezily.com/api/v2/orders' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9' \
--header 'Content-Type: application/json' \
--data-raw '{
  "external_reference": "C9759",
  "email": "orval_jerde@ko.org",
  "address": {
    "first_name": "Shalon",
    "last_name": "Kihn",
    "company": "Schaefer, Daugherty and Tillman",
    "street1": "Leonardo Garden",
    "street2": "",
    "city": "North Kory",
    "postcode": "80802-1677",
    "state": "NY",
    "country_code": "US",
    "phone": "0102030406"
  },
  "line_items": [
    {
      "external_reference": "I7366",
      "variant_reference": "TSRN_U_IGREEN_L",
      "quantity": 2,
      "designs": [
        {
          "key": "front",
          "url": "http://example.org/front_preview.png"
        }
      ]
    }
  ]
}
'
import http.client
import json

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = json.dumps({
  "email": "orval_jerde@ko.org",
  "external_reference": "C9759",
  "address": {
    "first_name": "Shalon",
    "last_name": "Kihn",
    "company": "Schaefer, Daugherty and Tillman",
    "street1": "Leonardo Garden",
    "street2": "",
    "city": "North Kory",
    "postcode": "80802-1677",
    "state": "NY",
    "country_code": "US",
    "phone": "0102030406"
  },
  "line_items": [
    {
      "external_reference": "I7366",
      "variant_reference": "TSRN_U_IGREEN_L",
      "quantity": 2,
      "designs": [
        {
          "key": "front",
          "url": "http://example.org/front_preview.png"
        }
      ]
    }
  ]
})
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9',
  'Content-Type': 'application/json'
}
conn.request("POST", "/api/v2/orders", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require "uri"
require "json"
require "net/http"

url = URI("https://plus.teezily.com/api/v2/orders")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Post.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
  "email": "orval_jerde@ko.org",
  "external_reference": "C9759",
  "address": {
    "first_name": "Shalon",
    "last_name": "Kihn",
    "company": "Schaefer, Daugherty and Tillman",
    "street1": "Leonardo Garden",
    "street2": "",
    "city": "North Kory",
    "postcode": "80802-1677",
    "state": "NY",
    "country_code": "US",
    "phone": "0102030406"
  },
  "line_items": [
    {
      "external_reference": "I7366",
      "variant_reference": "TSRN_U_IGREEN_L",
      "quantity": 2,
      "retail_price": "39.0",
      "designs": [
        {
          "key": "front",
          "url": "http://example.org/front_preview.png"
        }
      ]
    }
  ]
})

response = http.request(request)
puts response.read_body

The above command returns JSON structured like this:

{
  "id": 152,
  "external_reference": "C9759",
  "email": "orval_jerde@ko.org",
  "status": "pending",
  "address": {
    "first_name": "Shalon",
    "last_name": "Kihn",
    "company": "Schaefer, Daugherty and Tillman",
    "street1": "Leonardo Garden",
    "street2": "",
    "city": "North Kory",
    "postcode": "80802-1677",
    "state": "NY",
    "country_code": "US",
    "phone": "0102030406"
  },
  "line_items": [
    {
      "external_reference": "I7366",
      "variant_reference": "TSRN_U_IGREEN_L",
      "quantity": 2
    }
  ]
}

HTTP Request

POST https://plus.teezily.com/api/v2/orders

Order

Parameter Required Type Description
external_reference No String Your Order ID: O-xxx. Usefull to track orders. The API will raise an error if you have any other non-canceled orders with the same external_reference
email Yes String Buyer's e-mail
status No String Fulfillment internal status. do not submit this parameter for creation
address Yes Object An Address Object. The API will attempt to verify the address before accepting order
line_items Yes Array An array of Line Item Object

HTTP Response

Returns an Order object

Address

Parameter Required Type Description
first_name Yes String
last_name Yes String
company No String
phone No String
street1 Yes String
street2 Yes String If not present put an empty string
city Yes String
postcode Yes String Some countries do not require postcodes, in that case put an empty string
state No String Some countries do not require state codes, if it is the case the API will raise an error
country_code Yes String Formatted as ISO 3166-1 two-letter
skip_verified_delivery No Boolean Skip address delivery verification

Line Item

Parameter Required Type Description
external_reference No String Your unique line item ID. Usefull to track line items. The API will raise an error if you have any other non-canceled line items with the same external_reference
variant_reference Yes String The variant reference, see the Product endpoint
quantity Yes Integer
designs Yes Array An array of Design object
retail_price No String Value of single item as purchased from the end customer. Used if VAT is provided so we can generate an invoice for you

Design

Parameter Required Type Description
key Yes String Can be front, back, ... see our Design specification Side key column
url No String The url of the production design. Either url or file must be specified
file No File If you wish to submit your payload in multipart/form-data and attach directly the file in it. Either url or file must be specified
position No String Defaults to auto (more positional values coming soon)

Get a single order

curl --location --request GET 'https://plus.teezily.com/api/v2/orders/152' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
import http.client

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = ''
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
}
conn.request("GET", "/api/v2/orders/152", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require "uri"
require "net/http"

url = URI("https://plus.teezily.com/api/v2/orders/152")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Get.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"

response = http.request(request)
puts response.read_body

The above command returns JSON structured like this:

{
  "id": 152,
  "external_reference": "C9759",
  "shipping_method": "tracking",
  "email": "orval_jerde@ko.org",
  "status": "pending",
  "address": {
    "first_name": "Shalon",
    "last_name": "Kihn",
    "company": "Schaefer, Daugherty and Tillman",
    "street1": "Leonardo Garden",
    "street2": "",
    "city": "North Kory",
    "postcode": "80802-1677",
    "state": "NY",
    "country_code": "US",
    "phone": "0102030406"
  },
  "line_items": [
    {
      "external_reference": "I7366",
      "variant_reference": "TSRN_U_IGREEN_L",
      "quantity": 2
    }
  ]
}

HTTP Request

GET https://plus.teezily.com/api/v2/orders/{id}

Parameter Required Type Description
id String Yes The order ID

GET https://plus.teezily.com/api/v2/orders?external_reference={id}

Parameter Required Type Description
id String Yes The external order reference

HTTP Response

Returns an Order object

Get all orders

curl --location --request GET 'https://plus.teezily.com/api/v2/orders' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
import http.client

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = ''
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
}
conn.request("GET", "/api/v2/orders", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require "uri"
require "net/http"

url = URI("https://plus.teezily.com/api/v2/orders")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Get.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"

response = http.request(request)
puts response.read_body

The above command returns JSON structured like this:

{
  "results": [
    {
      "id": 152,
      "external_reference": "C9759",
      "shipping_method": "tracking",
      "email": "orval_jerde@ko.org",
      "status": "pending",
      "address": {
        "first_name": "Shalon",
        "last_name": "Kihn",
        "company": "Schaefer, Daugherty and Tillman",
        "street1": "Leonardo Garden",
        "street2": "",
        "city": "North Kory",
        "postcode": "80802-1677",
        "state": "NY",
        "country_code": "US",
        "phone": "0102030406"
      },
      "line_items": [
        {
          "external_reference": "I7366",
          "variant_reference": "TSRN_U_IGREEN_L",
          "quantity": 2
        }
      ]
    },
    {
      "id": 1,
      "external_reference": "C9346",
      "status": "pending",
      "shipping_method": "tracking",
      "email": "hobert@zemlakherzog.name",
      "address": {
        "first_name": "Nellie",
        "last_name": "Stoltenberg",
        "company": "Lueilwitz, Beier and Olson",
        "street1": "Elisha Wall",
        "street2": "",
        "city": "Kuhicfort",
        "postcode": "23988-4408",
        "state": "WI",
        "country_code": "US",
        "phone": "0102030406"
      },
      "line_items": [
        {
          "external_reference": "I4360",
          "variant_reference": "TSRN_U_IGREEN_L",
          "quantity": 3
        }
      ],
    },
  ],
  "count": 2
}

HTTP Request

GET https://plus.teezily.com/api/v2/orders

Available parameters, in addition to pagination:

Parameter Type Description
status String Filter by status

HTTP Response

Returns all Orders objects paginated

Parameter Type Description
results Array Orders objects
count Integer Number of results

Fulfill an order

curl --location --request PUT 'https://plus.teezily.com/api/v2/orders/152/fulfill' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
import http.client

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = ''
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
}
conn.request("PUT", "/api/v2/orders/152/fulfill", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

require "uri"
require "net/http"
url = URI("https://plus.teezily.com/api/v2/orders/152/fulfill")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Put.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"

response = http.request(request)
puts response.read_body

No content response

Once an order is created, in order to send it to production you need to use the fulfill API method.

HTTP Request

PUT https://plus.teezily.com/api/v2/orders/{id}/fulfill

Parameter Required Type Description
id String Yes The order ID

HTTP Response

No content.

Cancel an order

curl --location --request PUT 'https://plus.teezily.com/api/v2/orders/152/cancel' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
import http.client

conn = http.client.HTTPSConnection("plus.teezily.com", undefined)
payload = ''
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9'
}
conn.request("PUT", "/api/v2/orders/152/fulfill", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

require "uri"
require "net/http"
url = URI("https://plus.teezily.com/api/v2/orders/152/cancel")

http = Net::HTTP.new(url.host, url.port);
request = Net::HTTP::Put.new(url)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer d9119ab7-38df-4b05-ba8f-f374c022fff9"

response = http.request(request)
puts response.read_body

No content response

If an order is not yet produced you might still be able to cancel it using the following endpoint.

HTTP Request

PUT https://plus.teezily.com/api/v2/orders/{id}/cancel

Parameter Required Type Description
id Yes String The order ID

HTTP Response

No content.

Webhooks

Webhooks allow your system to receive notifications about certain events.

When an event occurs, the Teezily server will make a POST request to your defined URL containing a JSON object in the request body. Your server has to respond with HTTP status 200 OK, otherwise the request will be retried 12 times in increasing intervals (after 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 and 2048 minutes).

Creating a webhook

Webhook URL and secret key are defined in the settings of your Shop API.

If you already have a Shop API you can find it here otherwise create a new Shop API here.

Event list

Object Event Description
Order order_status_updated Order status updated
Order tracking_added Order tracking added (coming soon)
Order tracking_updated Order tracking added (coming soon)
Order tracking_deleted Order tracking deleted (coming soon)

HTTP Request

Parameter Type Description
event.uuid String UUID of the event.
event.object_id String ID of the event's object being sent over.
event.object_type String the type event's object type being sent over.
event.action String The name of the event.
event.message String Human readable message of event.
event.timestamp String When the event occured.
data String The latest data of the object. This data matches the same format as the GET requests.

Securing your Webhooks

Once your server is configured to receive payloads, it'll listen for any payload sent to the endpoint you configured. For security reasons, you probably want to limit requests to those coming from Teezily.

There are a few ways to go about this - for example, you could opt to whitelist requests from Teezily's IP address. An easy method is to and validate the information with a shared secret.

Setting your shared secret

You can generate the secret by running openssl rand -hex 20.

When your secret token is set into your account, Teezily will use it to create a hash signature with each payload body. Teezily uses an HMAC hexdigest to compute the hash sha256 signature with your provided secret.

Secret example

b19a1922449421904e94c3e139616b2faebd9c9d

The payload body signature is passed along with each request in the headers as X-Tzy-Signature. The signature format is: sha256={digest}.

Signature example

X-Tzy-Signature: sha256=c5e896be0dc286e8fe901d5dc1eae1422478475da21d64dfb4563c08e05ca12f

Validating payloads from Teezily

Next, compute a request body hash using your API key, and ensure that the hash from Teezily matches. Teezily uses an HMAC hexdigest to compute the hash. Always use "constant time" string comparisons, which renders it safe from certain timing attacks against regular equality operators.

Ruby on Rails example:

# The raw payload in the request body from Teezily
# ex: {"message":"test content"}
request_body = request.raw_post

# The signature in the `X-Tzy-Signature` header
# ex: sha256=3a0185ce8cc75387bc45dec59e4e119f8ba26823e10c0f60ba7522f001bf1627
request_signature = request.headers['X-Tzy-Signature']

# Your secret key
# You have set it with your ShopAPI callback URL in T4B application
your_secret = 'secret'

# Compute digest data
your_digest = OpenSSL::HMAC.hexdigest('sha256', your_secret, request_body)

# Prefix the digest to build your own signature
your_signature = "sha256=#{your_digest}"

# Compare with "constant time" string comparisons fonction:
# It should be true otherwise the request doesn't come from Teezily
Rack::Utils.secure_compare(request_signature, your_signature)

Where [SECRET_KEY] is your secret key.

Pagination

All endpoints allowing to fetch multiple results accept the pagination parameter below:

Parameter Type Description
page Integer A page number within the paginated result set (1 by default)
limit Integer Number of results per page (between 1 and 100, 25 by default)

Rate limits

Once throttled the API will return the following JSON:

{
  errors: [
    {
      code: "RateLimitError",
      title: "Too Many Requests",
      detail: "Your requests exceeded the rate of 60 by period of 60 seconds."
    }
  ]
}

The API enforces rate limits on the amount of requests that can be made. a 429 HTTP status code will be returned if throttled. Currently available rate is returned in response headers.

Time period Call limit
1 minute 60 calls

Response header

X-Tzy-Limit: 60

Status codes

The Teezily Plus API uses the following HTTP status codes:

HTTP Status Code Meaning
200 Ok
204 Ok -- No content
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- The following ressource requested is forbidden.
404 Not Found -- The specified ressource could not be found.
405 Method Not Allowed -- You tried to access a ressource with an invalid method.
406 Not Acceptable -- You requested a format that isn't json.
410 Gone -- The ressource requested has been removed from our servers.
418 I'm a teapot.
429 Too Many Requests -- You're requesting too many ressources! Slow down!
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

Design specification

The Design specification catalog cointains our different products with their recommended printing specifcations, If your designs don't match the recommended width and height we will adapt it the best possible way.

Product reference Name Side key
TSRN_U Round neck T-Shirt Unisex front
TSRN_U Round neck T-Shirt Unisex back
TSRN_W Round neck T-Shirt Woman front
TSRN_W Round neck T-Shirt Woman back
TSVN_U V-neck T-Shirt Unisex front
TSVN_U V-neck T-Shirt Unisex back
TSVN_W V-neck T-Shirt Woman front
TSVN_W V-neck T-Shirt Woman back
TSLS_U Long sleeved T-shirt Unisex front
TSLS_U Long sleeved T-shirt Unisex back
SWTR_U Sweatshirt Unisex front
SWTR_U Sweatshirt Unisex back
HOOD_U Hoodie Unisex front
HOOD_U Hoodie Unisex back
TSRN_K Kid T-Shirt front
TSRN_K Kid T-Shirt back
HOOD_K Kid Hoodie front
HOOD_K Kid Hoodie back
TKTP_U Tanktop Unisex front
TKTP_U Tanktop Unisex back
TKTP_W Tank top Woman front
TKTP_W Tank top Woman back
BODY_B Baby Onesies front
BODY_B Baby Onesies back
BODYLS_B Long Sleeved Baby Onesie front
BODYLS_B Long Sleeved Baby Onesie back
OTSRN_U Organic Round Neck T-Shirt Unisex front
OTSRN_U Organic Round Neck T-Shirt Unisex back
OTSRN_W Organic Round Neck T-Shirt Woman front
OTSRN_W Organic Round Neck T-Shirt Woman back
OTSVN_U Organic V-Neck T-Shirt Unisex front
OTSVN_U Organic V-Neck T-Shirt Unisex back
OTSVN_W Organic V-Neck T-Shirt Woman front
OTSVN_W Organic V-Neck T-Shirt Woman back
OTSLS_U Organic Long Sleeved T-Shirt Unisex front
OTSLS_U Organic Long Sleeved T-Shirt Unisex back
OSWTR_U Organic Sweater Unisex front
OSWTR_U Organic Sweater Unisex back
OHOOD_U Organic Hoodie Unisex front
OHOOD_U Organic Hoodie Unisex back
PTSRN_U Premium Round Neck T-Shirt Unisex front
PTSRN_U Premium Round Neck T-Shirt Unisex back
PSWTR_U Premium Sweater Unisex front
PSWTR_U Premium Sweater Unisex back
PHOOD_U Premium Hoodie Unisex front
PHOOD_U Premium Hoodie Unisex back
TOTE_U Tote Bag front
TOTE_U Tote Bag back
MUG_U Mug front
BMUG_U 15oz mug front
BTTMUG_U 15oz Two Tone Mug front
LMUG_U Tall Mug front
RMUG_U Colored Rim and Handle Mug front
TTMUG_U Two-tone mug front
DMAT3-2_U Doormat front
FLEEB_U Sherpa fleece blanket front
FMASK_U Face Mask front
IP5C_U iPhone 5 Case front
IP5SC_U iPhone 5S Case front
IP6C_U iPhone 6 Case front
IP6PC_U iPhone 6 Plus Case front
SGS5C_U Samsung Galaxy S5 Case front
SGS6C_U Samsung Galaxy S6 Case front
MIPICA2-1_U Microfiber Rectangle Pillow front
SAPICA1-1_U Satin Square Pillow front
NECKL_R Round Pendant Necklace front
NECKL_S Square Pendant Necklace front
ORCE_H Heart Shaped Ornament front
ORCE_R Round Shaped Ornament front