Biklo for developersTESTsandbox-api.biklo.comchange

Add images

POST/v1/products/{id}/images

scope products.write

Two ways, mixable in one call: images as URLs (referenced, nothing downloaded — the way supplier feeds work) and/or files as multipart uploads onto our storage. Position 0 is the primary image. For URLs, sending images with the product itself (POST /v1/products, PUT, or the batch) saves this request entirely — this endpoint is for uploads and for adding to what a listing has.

Request body#

FieldTypeRequiredDescription
imagesarrayrequiredThe images to attach, in the order you send them. Each entry may be an object with a url, or the URL string on its own.

Not supported An entry whose URL does not parse is SKIPPED silently — the call succeeds and returns how many were attached. Compare that count with what you sent rather than assuming.

images[].urlstringrequiredAn absolute URL we REFERENCE rather than download: the image is served from your host through our proxy, so it has to stay reachable for as long as the listing is live. Moving or deleting it breaks the listing.
replacebooleanoptionalDefaults to false, which APPENDS after the images already there. Send true to drop every existing image first — that is how you re-sync a product whose photos changed, and it is not reversible.
filesfile[]optionalUpload the images themselves instead, as multipart/form-data. replace works the same way.

Example

{
    "images": [
        {
            "url": "https://cdn.example.com/erp/mtb-front.jpg"
        },
        {
            "url": "https://cdn.example.com/erp/mtb-side.jpg"
        }
    ],
    "replace": false
}

Response#

FieldTypeDescription
attached_from_urlsintegerHow many were attached by link. COMPARE this with how many you sent: a URL that does not parse is skipped silently and the call still succeeds.
uploadedintegerHow many were uploaded as files.
imagesarrayThe listing's images AFTER the change, already in display order — so you never need a follow-up read to see the result.
images[].idintegerThe image id, for reordering and deletion.
images[].positionintegerIts place, 0 being the main photo.
images[].pathstring|nullWhere the file sits in our storage, when we host it.
images[].source_urlstring|nullThe URL we serve it FROM, when you attached it by link. Exactly one of these two is set.

Example

{
    "attached_from_urls": 2,
    "uploaded": 0,
    "images": [
        {
            "id": 108876841,
            "position": 0,
            "path": "files/237224/mtb-front.jpg",
            "source_url": null
        },
        {
            "id": 108876840,
            "position": 1,
            "path": null,
            "source_url": "https://cdn.example.com/erp/mtb-side.jpg"
        }
    ]
}

Failures use the shared error envelope — a machine-readable code and a request_id to quote to support. Error handling.

curl -X POST "https://sandbox-api.biklo.com/v1/products/711/images" \
  -H "Authorization: Bearer bk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
        {
            "url": "https://cdn.example.com/erp/mtb-front.jpg"
        },
        {
            "url": "https://cdn.example.com/erp/mtb-side.jpg"
        }
    ],
    "replace": false
}'
<?php

$ch = curl_init('https://sandbox-api.biklo.com/v1/products/711/images');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'POST',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer bk_test_YOUR_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
      'images' => [
        0 => [
          'url' => 'https://cdn.example.com/erp/mtb-front.jpg',
        ],
        1 => [
          'url' => 'https://cdn.example.com/erp/mtb-side.jpg',
        ],
      ],
      'replace' => false,
    ]),
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);
using System.Net.Http;
using System.Net.Http.Headers;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "bk_test_YOUR_KEY");

var request = new HttpRequestMessage(
    new HttpMethod("POST"),
    "https://sandbox-api.biklo.com/v1/products/711/images");

request.Content = new StringContent(
    @"{ ""images"": [ { ""url"": ""https://cdn.example.com/erp/mtb-front.jpg"" }, { ""url"": ""https://cdn.example.com/erp/mtb-side.jpg"" } ], ""replace"": false }",
    System.Text.Encoding.UTF8,
    "application/json");

var response = await client.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
import requests

response = requests.post(
    "https://sandbox-api.biklo.com/v1/products/711/images",
    headers={"Authorization": "Bearer bk_test_YOUR_KEY"},
    json={
        "images": [
            {
                "url": "https://cdn.example.com/erp/mtb-front.jpg"
            },
            {
                "url": "https://cdn.example.com/erp/mtb-side.jpg"
            }
        ],
        "replace": False
    },
)

data = response.json()
const response = await fetch("https://sandbox-api.biklo.com/v1/products/711/images", {
  method: "POST",
  headers: {
    Authorization: "Bearer bk_test_YOUR_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
      "images": [
          {
              "url": "https://cdn.example.com/erp/mtb-front.jpg"
          },
          {
              "url": "https://cdn.example.com/erp/mtb-side.jpg"
          }
      ],
      "replace": false
  })
});

const data = await response.json();
import java.net.http.*;
import java.net.URI;

var client = HttpClient.newHttpClient();

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://sandbox-api.biklo.com/v1/products/711/images"))
    .header("Authorization", "Bearer bk_test_YOUR_KEY")
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString(
        "{ \"images\": [ { \"url\": \"https://cdn.example.com/erp/mtb-front.jpg\" }, { \"url\": \"https://cdn.example.com/erp/mtb-side.jpg\" } ], \"replace\": false }"))
    .build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());