Skip to content

Local development

Build and test an integration on your own machine, a staging host or a stand, without spending money or plan generations: use test keys. A test job goes through the real API - the same validation, image checks, idempotency and answers - and ends in a simulated result.

Get a test key

You issue test keys yourself, in the client portal:

  1. Sign in at https://app.ziex-tryon.com with your live secret key (tryon_…).
  2. Open the API Keys page and use the test keys block. Create a key; its secret (tryon_test_…) is shown once - copy it then. The list afterwards shows only the publishable half (pk_test_…).
  3. You can hold up to 3 test keys per account (a fourth is 409 TEST_KEY_LIMIT). To rotate one, delete it and create another.

A test key can not sign in to the portal (401 TEST_KEY_NOT_ALLOWED) - that is why you issue it from the live key's session and hand the test key to a developer, who never needs the portal.

Test secret key Test publishable key
Looks like tryon_test_ + 32 characters pk_test_ + 32 characters
Use it from Your server or a local script A page in a browser
Needs Origin / Referer No Yes (but no bound domain)

If test keys are not available for your account, the key is refused as an unknown one (401 INVALID_API_KEY) and the portal block is hidden.

What a test try-on does

Use the API exactly as in Quickstart B, with a test key. What differs:

  • No money, no plan. A test job takes no generation, no daily cap and no share of anyone's limits, and is left out of all statistics. It is never refunded because nothing was taken.
  • The shopper's photo must be an upload - a data URI - for a test secret key as well as for a publishable key. A URL is 422 PHOTO_UPLOAD_REQUIRED. The garment may be a URL: nothing is downloaded in test mode.
  • The result is simulated. After about 3 seconds the job completes with your own uploaded photo as result_url, or, if that is not possible, with a fixed placeholder image (https://api.ziex-tryon.com/embed/test-result.png). The category is still checked: an unknown one is 422 CATEGORY_UNSUPPORTED.
  • Simulate a failure. Add "test_outcome": "failed" to the body; the job ends failed with error_code GENERATION_FAILED. Any other value counts as completed. A live key ignores this field.
  • Isolation. A test key reads only the test jobs of its own key; a live key never sees a test job, and the other way round. A job of the other mode is 404, not 403.
  • Own limits. 30 submits a minute per IP (counted apart from your live traffic), 60 created jobs per 10 minutes per (test publishable key, IP), and the key's hourly limit (100 by default).
  • Files you upload with a test key are deleted after 72 hours.
  • The widget shows a TEST ribbon when its key starts with pk_test_, and GET /api/v1/tryon/domain-check returns "mode": "test".
import base64
import uuid

import requests

API = "https://api.ziex-tryon.com/api/v1/tryon"
HEADERS = {"X-API-Key": "tryon_test_YOUR_TEST_KEY", "User-Agent": "shop-example-dev/1.0"}

with open("shopper.jpg", "rb") as f:
    photo = "data:image/jpeg;base64," + base64.b64encode(f.read()).decode("ascii")

r = requests.post(
    API,
    json={
        "model_image": photo,                       # a test key needs an upload
        "garment_image": "https://shop.example/images/linen-shirt.jpg",
        "category": "tops",
        "test_outcome": "completed",                # or "failed"
    },
    headers={**HEADERS, "Idempotency-Key": uuid.uuid4().hex},
    timeout=60,
)
print(r.status_code, r.json())

Hosts a test key accepts

A browser call always names its page in the Origin (or Referer) header. The key's rule judges that host.

A live key works only on its bound domain (the domain itself, www. and any subdomain). A test key works from:

  1. any development host, with no registration:

    Host Examples
    localhost and *.localhost localhost, bitrix.localhost
    *.test, *.local, *.internal shop.test, my-shop.local, stand.internal
    Loopback and private IP addresses 127.0.0.1, [::1], 10.0.0.5, 172.16.4.2, 192.168.1.10
    The opaque origin null a page opened from file://, a sandboxed iframe
  2. any domain registered for your account (with www. and subdomains);

  3. any host in the key's own allowed_hosts list (up to 20), edited in the portal's test keys block.

Anything else is 403 DOMAIN_NOT_ALLOWED. A test publishable key with no Origin or Referer at all is 403 ORIGIN_REQUIRED.

A live key used from a development host is refused with 403 DOMAIN_NOT_ALLOWED - on a local machine, use a test key.

How a host is compared

The same rule applies to the Origin, to allowed_hosts and to the site address you send to GET /integration:

  • The port is ignored. http://localhost:3000 and http://localhost:8080 are both localhost. Do not write a port into allowed_hosts; if you do, it is dropped.
  • Internationalised names are compared as a browser writes them (UTS #46, punycode). магазин.рф is stored and matched as xn--80aairftm.xn--p1ai, and the portal shows both. Upper case, a trailing dot and a scheme are removed; straße.de becomes xn--strae-oqa.de, as in a browser.
  • A name covers its www. and its subdomains. shop.example also admits www.shop.example and staging.shop.example. Wildcards such as *.shop.example are not accepted - the plain name already covers them.
  • An IP address matches only itself. 203.0.113.5 does not admit 1.203.0.113.5.
  • At most 20 entries in allowed_hosts.

Pages from file://

A page opened straight from the disk sends Origin: null. A test key accepts it, so a plain HTML file with a pk_test_… key works without a web server. A live key does not (403). Serving the same folder from localhost works too.

Webhooks on your own machine

A webhook is never delivered to localhost or a private address. The delivery refuses every address that is not public (loopback, private ranges, link-local and cloud-metadata addresses, and names that resolve to them), and the same check runs when you register the address - a webhook_url that is not public is 422 VALIDATION_ERROR. The refusal is not retried.

Two ways to develop against webhooks:

  • A tunnel. Run a tunnelling tool that gives your local port a public HTTPS address, and use that address as webhook_url. Receivers on a laptop behind a router work this way.
  • Polling. Skip the webhook while developing and poll GET /api/v1/tryon/status/{job_id}. A test job completes in a few seconds, so this is the quickest loop.

A per-request webhook_url works with a test secret key and is signed like a live one. Webhook endpoints you register in the portal have a mode, live or test, chosen when you create them: a test job calls only test endpoints, and a live job only live ones. A test publishable key cannot send webhook_url at all (403 WEBHOOK_NOT_ALLOWED).

Worked example: a 1C-Bitrix stand

A developer runs a 1C-Bitrix demo on http://bitrix.localhost:8588 and installs the Ziex Try-On module. The module has to check that its key may be used on this site. It calls GET /api/v1/tryon/integration with the test secret key and the site address:

curl -sS 'https://api.ziex-tryon.com/api/v1/tryon/integration?site_url=http://bitrix.localhost:8588' \
  -H 'X-API-Key: tryon_test_YOUR_TEST_KEY' \
  -H 'User-Agent: shop-example-dev/1.0'
{
  "key": { "mode": "test", "publishable_key": "pk_test_…", "domain": null },
  "site": { "url": "http://bitrix.localhost:8588", "host": "bitrix.localhost", "allowed": true, "reason": null },
  "plan": { "name": null, "generations_limit": null, "generations_remaining": null, "generations_used": 0, "resets_at": null }
}

The answer is shortened: it also carries the client name, the widget address and the capabilities document. The port is ignored and bitrix.localhost is a development host, so site.allowed is true and the module can print the test publishable key into the page. The widget then runs from bitrix.localhost with pk_test_…, shows the TEST ribbon, and every try-on ends in a simulated result without touching the plan.

With the same site address and a live key the answer would be site.allowed: false with site.reason dev_host (DOMAIN_NOT_ALLOWED for a request). When you move from the stand to the real shop, switch the module to the live secret key and bind the live key to the shop's domain.

Checklist before going live

  • Replace pk_test_… with the live pk_… and tryon_test_… with the live tryon_….
  • Confirm the live key is bound to the shop's domain: domain check.
  • Create live webhook endpoints, or keep per-request webhook_url on a public address.
  • Remove the secret key from anything that runs in a browser.