Skip to navigation

Going to production

The things that separate a working integration from one that survives a bad day.

A quickstart integration works. Everything below is what it does not yet handle, in rough order of how expensive it is to get wrong.

Correctness

1

Check job.status before reading job.output

A failed analysis arrives as HTTP 200 with "status": "failed". Nothing in your HTTP client will raise. Code that assumes 2xx means “results are here” carries a failed job forward silently. Asynchronous jobs

2

Send an Idempotency-Key on every submission, derived from your own record

Derived from the durable business operation (order-48213-skin), not from the attempt. A fresh key per retry is no idempotency at all, and a submission timeout without one is a double charge. Idempotency

3

Branch on error.code, never on error.message

Messages are human text and change without notice. Codes are the contract. Treat an unrecognised code as a failure of its status class rather than crashing. Errors

4

Persist the job id before you start waiting

It is the only handle to work you have already paid for, and a client-side timeout does not cancel the job. Store it next to the order or session that caused it.

5

Get the score direction right

Skin scores are 0–10 and higher means the concern is less visible. Reversing this in a customer-facing UI is a bug that passes review because both directions look plausible. Skin analysis

Resilience

1

Honour Retry-After

On a submission, a poll, a 429 and a 503, it says when to come back. Ignoring them turns brief saturation into sustained saturation, and earns 429 RATE_LIMITED.

2

Back off with full jitter, and retry only what is retryable

408/429/5xx/transport/409 IDEMPOTENCY_IN_PROGRESS. Nothing else. Retrying a 400 or a 409 IDEMPOTENCY_KEY_REUSED repeats a request that is wrong, at cost. Retries and backoff

3

Set two deadlines: per-request and per-operation

A per-request timeout alone gives an unbounded loop of quick calls. An operation deadline alone lets one hung socket eat the budget.

4

Do not treat 503 on a poll as expiry

503 SERVICE_UNAVAILABLE means ask again. Only 404 JOB_NOT_FOUND on a poll, or 404 ASSET_NOT_FOUND on a download, past the expires_at means the work is gone. Confusing them discards completed work.

5

Handle 402 as an operational alert, not a retry

402 INSUFFICIENT_CREDITS will not clear on its own. Page someone who can top up. Credits and usage

6

Send If-None-Match on polls

A 304 is much cheaper for both sides and keeps you under rate limits in a busy period.

7

Pace submissions by the rate-limit headers where you get them

Where your account has rate-limit headers on, RateLimit says how much of each policy is left and when it refills. Slow down before the 429, not after it. Without the headers, 429 and Retry-After are the signal. Rate limits

8

If you use webhooks, verify, dedupe, and keep polling as the fallback

Verify the signature against the raw body, dedupe on event.id, answer 2xx within 10 seconds and do the work afterwards. Delivery is at least once and unordered, so keep a slow poll for any job whose event you have not seen. Webhooks

Security

1

The key is only ever server-side

Read it from a secret manager at startup. Not in a browser bundle, a mobile binary, a client config, an image layer, or a committed file. Authentication

2

If you have a front end, you have a BFF

And your BFF authorizes your own users, because this API cannot: within your account there is no isolation between your customers. Serving a browser or mobile client

3

Plan a rotation you can execute

Two keys valid at once during the swap, so you are never down to zero. Rotate on suspicion and on staff changes, and revoke the old key in the Platform once nothing uses it. A revoked or expired key answers 401 INVALID_API_KEY at once.

4

Narrow each key

Give a key only the product scopes it calls, and, where the Platform offers it, an expiry and a network allowlist. A call from outside the allowlist is 403 API_KEY_IP_NOT_ALLOWED. Authentication

5

Delete what you no longer need

Assets and jobs expire on their own. If you have finished with one sooner, or a person asks you to erase their data, delete the job (it takes its result images with it) and any input Asset. Jobs, Assets

6

Never log the key, the bytes, or landmarks — not even partially

A key prefix plus a length is a head start. Log request_id and the job id; they are enough for support and are not personal data. Privacy and retention

7

Store only the fields you use

A skin result carries no demographic inference, but it is still derived from an identifiable face. Keep the concerns you display and the analysis_version, not the whole output and every result image. Skin analysis

Operations

1

Record request_id on every failure

It is on every error body and in the X-Request-Id header. Without it a support conversation starts with an archaeology exercise.

2

Alert on rates, not on individual errors

A single 503 is weather. A sustained 503 rate, a rising 429 rate, or a 402 at all are the three worth waking someone for.

3

Watch GET /v1/usage for shape changes, not for billing

A tripled jobs_accepted count or a growing jobs_failed share is usually your own integration changing behaviour. Read only final days as settled. It is not invoice authority.

4

Watch the credit balance

Where your account has the balance endpoint, read GET /v1/credits/balance on a schedule and alert on low_balance before submissions start answering 402. Usage and credits

5

Keep live and test apart

Use pk_test_ keys in development and CI and pk_live_ keys only in production. The two environments share nothing: an id from one is a 404 in the other. Test mode

Before you promise anything to your own customers

  • Makeup pixel parity is not proven for any category. Do not claim your rendering matches an existing browser try-on. Makeup try-on
  • Makeup is three categories: foundation, lipstick and lip_liner. No textures, no other products.
  • There is no URL submission. Upload the bytes to POST /v1/assets, from your server.
  • Cancel does not stop processing that has started. A job already being processed finishes and bills. Listing, cancel, delete, webhooks and the credit balance are enabled per account; confirm which ones yours has before you depend on them.
  • No SLA, uptime or latency figure is published here. If you need one, it comes from your commercial agreement, not from this site.