ZeDaq.ai
Menswear shopper viewing a suit on a fashion store website

Calling a Try-On API From PHP: A Worked Example

By the ZeDaq.ai team · · 5 min read

On this page
  1. What does the request look like?
  2. How do you send the request with curl?
  3. How do you handle a shopper's upload?
  4. What do you do when the API returns 202?
  5. How should you run this inside WordPress?
  6. How do you put it together in one endpoint?
  7. How do you test it before launch?
  8. What errors should you expect?

Calling the ZeDaq.ai try-on API from PHP takes one curl POST: base64-encode the shopper photo and the product image, send them as JSON with an x-api-key header, then poll if the API answers 202. This worked example gives you a function you can drop into a WordPress plugin or any PHP backend.

It assumes you have read the overview in our virtual try-on API guide for developers. The full contract is in the API reference.

What does the request look like?

The endpoint is POST /api/v1/tryon on https://www.zedaq.ai. The body is JSON with person_image_base64, product_image_base64 and an optional garment_type of upper_body, lower_body or dresses. The API docs say JPEG or PNG works, and a full-length photo about 800px wide is a good size (API reference).

Your key goes in the x-api-key header. Keep it on the server and never print it into a template.

How do you send the request with curl?

Store the key in an environment variable or your config, not in the file. Then use this function.

<?php
function zedaq_request(string $method, string $path, ?array $body = null): array {
    $ch = curl_init('https://www.zedaq.ai' . $path);
    $headers = ['x-api-key: ' . getenv('ZEDAQ_API_KEY')];
    if ($body !== null) {
        $headers[] = 'Content-Type: application/json';
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
    }
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 60,
    ]);
    $raw = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return [$status, json_decode((string) $raw, true) ?? []];
}

The function returns the HTTP status and the decoded JSON, so the caller can decide what to do with a 200, 202 or error.

How do you handle a shopper's upload?

A browser form sends the photo as multipart/form-data, which PHP exposes in $_FILES. Read the temporary file, encode it, and pass it to the function above.

<?php
$person  = base64_encode(file_get_contents($_FILES['photo']['tmp_name']));
$product = base64_encode(file_get_contents('/var/www/images/linen-shirt.jpg'));
[$status, $data] = zedaq_request('POST', '/api/v1/tryon', [
    'person_image_base64'  => $person,
    'product_image_base64' => $product,
    'garment_type'         => 'upper_body',
]);

Check the file type and size before you encode it. Reject anything that is not a JPEG or PNG.

What do you do when the API returns 202?

A 200 response includes image_base64 and mime_type. A 202 means the render is still running, and the body holds a job_id and a poll_url. The docs say to poll that URL about every 2 seconds until it returns 200 (API reference).

<?php
if ($status === 202) {
    for ($i = 0; $i < 30; $i++) {
        sleep(2);
        [$status, $data] = zedaq_request('GET', $data['poll_url'] ?? '');
        if ($status !== 202) break;
    }
}
if ($status === 200) {
    header('Content-Type: ' . $data['mime_type']);
    echo base64_decode($data['image_base64']);
}

The polling request also needs the x-api-key header, which the function already adds. Usage is only charged when a render succeeds.

How should you run this inside WordPress?

Call the function from a custom REST route or an admin-ajax handler, never from the page itself. That keeps the key out of the browser and lets you check the nonce and the file before you spend a render.

Long loops tie up a PHP worker. For busy stores, return the job_id to the browser and let it ask your own endpoint for the result.

If you only need a button on a WooCommerce page and no custom code, the one-script-tag widget is simpler.

How do you put it together in one endpoint?

Here is a complete handler you can mount in a plain PHP file or a framework controller. It checks the upload, calls the API, waits if needed and returns JSON for your front end.

<?php
header('Content-Type: application/json');
$file = $_FILES['photo'] ?? null;
$type = $file ? mime_content_type($file['tmp_name']) : '';
if (!$file || !in_array($type, ['image/jpeg', 'image/png'], true)) {
    http_response_code(400);
    echo json_encode(['error' => 'bad_photo']);
    exit;
}
[$status, $data] = zedaq_request('POST', '/api/v1/tryon', [
    'person_image_base64'  => base64_encode(file_get_contents($file['tmp_name'])),
    'product_image_base64' => base64_encode(file_get_contents($productPath)),
    'garment_type'         => $garmentType,
]);
while ($status === 202) {
    sleep(2);
    [$status, $data] = zedaq_request('GET', $data['poll_url'] ?? '');
}
http_response_code($status);
echo json_encode($data);

Set $productPath and $garmentType from your own catalog on the server, not from the request. Add a maximum number of polling attempts before you go live so a stuck job cannot hold a worker forever.

How do you test it before launch?

Run it from the command line first with a test photo, then from the browser. Check four cases: a good photo, a wrong file type, a wrong key and a product image that fails to load.

Log the status code and error code for each call. These logs will also tell you how many renders you really use, which is the input to your plan choice.

What errors should you expect?

Errors come back as JSON with an error code and a message. The docs list codes such as missing_api_key, invalid_api_key, domain_not_allowed and tryon_cap_reached. Log the code, and show shoppers a calm message instead of the raw text.

If your team also runs other languages, the same flow is shown in a Python version of this example and a Node.js version.

Write size guides that help shoppers pick the right garment before they ever upload a photo, as explained in how to write size guides that reduce returns.

Get an API key and test your PHP code

FAQs

Can I send the image as multipart instead of base64?

The documented request is JSON with base64 strings. Accept the multipart upload from your shopper, then encode it on your server before you call the API.

Which PHP version do I need?

The example uses only curl and standard functions. It runs on any PHP version where the curl extension is enabled.

Where should I store the API key?

Use an environment variable or a config value outside your web root. Never put it in a theme file that prints to the page.

Do failed renders use up my allowance?

The API reference says usage is only charged once a render succeeds. Monthly caps still depend on your plan.

Try ZeDaq.ai free

Virtual try-on and Find my size for your store, plus client, order and invoicing tools for tailors, in one subscription.

Related articles