Skip to content

Shipments

Create, list, retrieve, track, and cancel shipments, and fetch their printable labels. Shipments are carrier-agnostic: the same request and response shapes apply regardless of the underlying carrier. Every shipment is created and addressed under a single carrier account (a License); it is identified in the API by its uuid.

All endpoints on this page require client-credentials authentication and act under one resolved license. Responses use the standard { "data": ... } envelope; errors use { "error": { ... } }.


GET /shipments

List the caller's shipments, paginated. Covers every license the account owns unless license_key narrows it. Auth: client credentials. License: optional.

Parameters

Query:

FieldTypeRequiredDescription
license_keystringNolicenses.key to narrow the results to one carrier account. Omit it and the listing covers every license you own.
filter[active]booleanNoFilter by active state. true returns only active shipments, false only inactive. Omit for all.
filter[type]integerNoService type. 1 delivery, 2 collection. filter[type]=2 is how you list returns.
sortstringNoSort key applied by the repository (e.g. a column name, optionally --prefixed for descending).
per_pageintegerNoItems per page. Default 25, capped at 100.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments?filter[active]=true&per_page=25' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const url = new URL('https://app.shipos.co.il/api/v2/shipments')
url.searchParams.set('filter[active]', 'true')
url.searchParams.set('per_page', '25')

const response = await fetch(url, {
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    Accept: 'application/json',
  },
})

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: shipments, meta } = await response.json()
console.log(`${shipments.length} of ${meta.total} shipments`)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$payload = json_decode($client->get('shipments', [
    'query' => ['filter[active]' => 'true', 'per_page' => 25],
])->getBody()->getContents(), true);

echo count($payload['data']), ' of ', $payload['meta']['total'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$payload = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get('https://app.shipos.co.il/api/v2/shipments', [
        'filter[active]' => 'true',
        'per_page' => 25,
    ])
    ->throw()
    ->json();

logger()->info(count($payload['data']).' of '.$payload['meta']['total']);
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/shipments",
    params={"filter[active]": "true", "per_page": 25},
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()
payload = response.json()

print(len(payload["data"]), "of", payload["meta"]["total"])
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/shipments?filter[active]=true&per_page=25", nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(body)) // {"data":[...],"meta":{...}}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsListShipments {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/shipments"
                + "?filter%5Bactive%5D=true&per_page=25"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":[...],"meta":{...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>(
        "shipments?filter[active]=true&per_page=25")
    ?? throw new InvalidOperationException("Empty response");

var meta = payload.RootElement.GetProperty("meta");
Console.WriteLine($"total: {meta.GetProperty("total").GetInt32()}");
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/shipments")
uri.query = URI.encode_www_form("filter[active]" => "true", "per_page" => 25)

request = Net::HTTP::Get.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

payload = JSON.parse(response.body)
puts "#{payload["data"].size} of #{payload.dig("meta", "total")}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let payload: Value = reqwest::Client::new()
        .get("https://app.shipos.co.il/api/v2/shipments")
        .query(&[("filter[active]", "true"), ("per_page", "25")])
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    let count = payload["data"].as_array().map_or(0, Vec::len);
    println!("{} of {}", count, payload["meta"]["total"]);
    Ok(())
}

Response 200

A paginated collection. Each element is a shipment object; pagination metadata is included under links and meta.

json
{
  "data": [
    {
      "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
      "tracking_code": "66747921",
      "carrier": { "id": 3, "name": "HFD" },
      "status": { "code": "1", "description": "Created", "is_delivered": false },
      "service_type": "1",
      "is_active": true,
      "recipient": {
        "name": "Dana Levi",
        "phone": "0521234567",
        "company": null,
        "address": {
          "street": "Herzl",
          "number": "10",
          "city": "Tel Aviv",
          "state": null,
          "zip": "6100000",
          "country": "IL"
        }
      },
      "pickup_point_id": null,
      "packages": 1,
      "cod": null,
      "order": { "id": "1042", "number": "1042" },
      "references": { "external_id": null },
      "short_tracking_code": null,
      "label_generated": false,
      "collection_status": null,
      "collected_at": null,
      "ready_at": null,
      "created_at": "2026-07-29T09:14:00.000000Z",
      "updated_at": "2026-07-29T09:14:00.000000Z"
    }
  ],
  "links": { "first": "...", "last": "...", "prev": null, "next": null },
  "meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenlicense_key not owned by the caller, or the resolved license is inactive/expired, or the account has no active license.
422validation_failedAccount has more than one license and license_key was omitted.

POST /shipments

Create a shipment with the carrier. Auth: client credentials. License: required.

The request body is the carrier-facing { ship_data, order } envelope. ship_data is the flat carrier payload (contact, destination, service options); order is the order context persisted alongside the shipment, and its shipping block is the recipient snapshot written onto the shipment row.

Creation is idempotent and crash-safe. Pass an optional Idempotency-Key request header to bind the create to a client-chosen key; otherwise a stable fingerprint of the body is used. See Idempotency for the full contract, including the idempotency_key_conflict behaviour.

Server-owned fields

ship_data.license_key, ship_data.license_id, and ship_data.shipping_id are ignored if sent — the license is resolved from your credentials (and the top-level license_key), never from the body.

Parameters

Top-level body:

FieldTypeRequiredDescription
license_keystringConditionallicenses.key of the carrier account. Optional with a single active license; required with more than one. Max 255.
ship_dataobjectYesCarrier-facing shipment payload. See below.
orderobjectYesOrder envelope. See below.

ship_data:

FieldTypeRequiredDescription
ship_data.contact_namestringYesRecipient contact name. Max 255.
ship_data.contact_phonestringYesRecipient contact phone. Max 32.
ship_data.contact_mailstring (email)NoRecipient email. Max 255.
ship_data.streetstringConditionalDestination street. Required unless ship_data.pickup is provided. Max 255.
ship_data.numberstringNoDestination house/building number. Max 32.
ship_data.citystringConditionalDestination city. Required unless ship_data.pickup is provided. Max 255.
ship_data.entrancestringNoBuilding entrance. Max 32.
ship_data.floorstringNoFloor. Max 32.
ship_data.apartmentstringNoApartment. Max 32.
ship_data.companystringNoDestination company name. Max 255.
ship_data.typeintegerYesDirection: 1 = regular delivery, 2 = collection.
ship_data.returnintegerYesReturn mode: 1 = single, 2 = round-trip.
ship_data.packagesintegerYesNumber of packages. Min 1, max 100.
ship_data.pickupstringNoPickup-point id (drives pickup-point deliveries). Max 64. When set, street/city are not required.
ship_data.pickup_addressstringNoHuman-readable pickup-point address. Max 255.
ship_data.notestringNoDelivery note. Max 1000.
ship_data.extra_notestringNoAdditional note. Max 1000.
ship_data.urgentbooleanNoRequest urgent handling.
ship_data.motorintegerNoMotorcycle courier flag: 0 or 1.
ship_data.collectbooleanNoCollection/COD flag.
ship_data.exaction_datedateNoRequested pickup/collection date.
ship_data.delivery_timestringNoRequested delivery time window. Max 64.
ship_data.IsManualbooleanNoMarks the shipment as manually entered.

order:

FieldTypeRequiredDescription
order.idstringNoYour order identifier. Max 64. Used as the neutral order reference.
order.numberstringNoHuman order number. Max 64. Falls back to order.id for the reference.
order.statusstringNoOrder status. Max 64.
order.currencystringNoISO currency. Max 8.
order.totalnumberNoOrder total. Min 0.
order.customer_idmixedNoYour customer identifier.
order.customer_notestringNoCustomer note. Max 1000.
order.sourcestringNoOrder source/platform label. Max 32.
order.shippingobjectYesRecipient snapshot (see below).
order.billingobjectNoBilling block; mirrors shipping, validated loosely.
order.order_itemsarrayNoLine items (see below).

order.shipping (recipient snapshot; all fields nullable):

FieldTypeRequiredDescription
order.shipping.first_namestringNoRecipient first name. Max 255.
order.shipping.last_namestringNoRecipient last name. Max 255.
order.shipping.companystringNoRecipient company. Max 255.
order.shipping.phonestringNoRecipient phone. Max 32.
order.shipping.emailstring (email)NoRecipient email. Max 255.
order.shipping.address_1stringNoAddress line 1. Max 255.
order.shipping.address_2stringNoAddress line 2. Max 255.
order.shipping.citystringNoCity. Max 255.
order.shipping.statestringNoState/region. Max 255.
order.shipping.postcodestringNoPostcode. Max 16.
order.shipping.countrystringNoISO-2 country code. Max 2.

order.order_items[] (all fields nullable):

FieldTypeRequiredDescription
order.order_items.*.skustringNoItem SKU. Max 255.
order.order_items.*.namestringNoItem name. Max 255.
order.order_items.*.quantityintegerNoQuantity. Min 1.
order.order_items.*.pricenumberNoUnit price. Min 0.
order.order_items.*.totalnumberNoLine total. Min 0.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f' \
--data '{
  "license_key": "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
  "ship_data": {
    "contact_name": "Dana Levi",
    "contact_phone": "0521234567",
    "contact_mail": "dana@example.com",
    "street": "Herzl",
    "number": "10",
    "city": "Tel Aviv",
    "floor": "3",
    "apartment": "12",
    "type": 1,
    "return": 1,
    "packages": 1,
    "note": "Leave with doorman"
  },
  "order": {
    "id": "1042",
    "number": "1042",
    "currency": "ILS",
    "total": 199.90,
    "source": "custom",
    "shipping": {
      "first_name": "Dana",
      "last_name": "Levi",
      "phone": "0521234567",
      "email": "dana@example.com",
      "address_1": "Herzl 10",
      "city": "Tel Aviv",
      "postcode": "6100000",
      "country": "IL"
    },
    "order_items": [
      { "sku": "SKU-1", "name": "Blue mug", "quantity": 2, "price": 49.95, "total": 99.90 }
    ]
  }
}'
js
// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://app.shipos.co.il/api/v2/shipments', {
  method: 'POST',
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    Accept: 'application/json',
    'Content-Type': 'application/json',
    'Idempotency-Key': '9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f',
  },
  body: JSON.stringify({
    license_key: 'b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c',
    ship_data: {
      contact_name: 'Dana Levi',
      contact_phone: '0521234567',
      contact_mail: 'dana@example.com',
      street: 'Herzl',
      number: '10',
      city: 'Tel Aviv',
      floor: '3',
      apartment: '12',
      type: 1,
      return: 1,
      packages: 1,
      note: 'Leave with doorman',
    },
    order: {
      id: '1042',
      number: '1042',
      currency: 'ILS',
      total: 199.9,
      source: 'custom',
      shipping: {
        first_name: 'Dana',
        last_name: 'Levi',
        phone: '0521234567',
        email: 'dana@example.com',
        address_1: 'Herzl 10',
        city: 'Tel Aviv',
        postcode: '6100000',
        country: 'IL',
      },
      order_items: [
        { sku: 'SKU-1', name: 'Blue mug', quantity: 2, price: 49.95, total: 99.9 },
      ],
    },
  }),
})

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: shipment } = await response.json()
console.log(shipment.uuid, shipment.tracking_code)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
        'Idempotency-Key' => '9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f',
    ],
]);

$shipment = json_decode($client->post('shipments', [
    'json' => [
        'license_key' => 'b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c',
        'ship_data' => [
            'contact_name' => 'Dana Levi',
            'contact_phone' => '0521234567',
            'contact_mail' => 'dana@example.com',
            'street' => 'Herzl',
            'number' => '10',
            'city' => 'Tel Aviv',
            'floor' => '3',
            'apartment' => '12',
            'type' => 1,
            'return' => 1,
            'packages' => 1,
            'note' => 'Leave with doorman',
        ],
        'order' => [
            'id' => '1042',
            'number' => '1042',
            'currency' => 'ILS',
            'total' => 199.90,
            'source' => 'custom',
            'shipping' => [
                'first_name' => 'Dana',
                'last_name' => 'Levi',
                'phone' => '0521234567',
                'email' => 'dana@example.com',
                'address_1' => 'Herzl 10',
                'city' => 'Tel Aviv',
                'postcode' => '6100000',
                'country' => 'IL',
            ],
            'order_items' => [
                ['sku' => 'SKU-1', 'name' => 'Blue mug', 'quantity' => 2, 'price' => 49.95, 'total' => 99.90],
            ],
        ],
    ],
])->getBody()->getContents(), true)['data'];

echo $shipment['uuid'], ' ', $shipment['tracking_code'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$shipment = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
    'Idempotency-Key' => '9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f',
])
    ->acceptJson()
    ->post('https://app.shipos.co.il/api/v2/shipments', [
        'license_key' => 'b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c',
        'ship_data' => [
            'contact_name' => 'Dana Levi',
            'contact_phone' => '0521234567',
            'contact_mail' => 'dana@example.com',
            'street' => 'Herzl',
            'number' => '10',
            'city' => 'Tel Aviv',
            'floor' => '3',
            'apartment' => '12',
            'type' => 1,
            'return' => 1,
            'packages' => 1,
            'note' => 'Leave with doorman',
        ],
        'order' => [
            'id' => '1042',
            'number' => '1042',
            'currency' => 'ILS',
            'total' => 199.90,
            'source' => 'custom',
            'shipping' => [
                'first_name' => 'Dana',
                'last_name' => 'Levi',
                'phone' => '0521234567',
                'email' => 'dana@example.com',
                'address_1' => 'Herzl 10',
                'city' => 'Tel Aviv',
                'postcode' => '6100000',
                'country' => 'IL',
            ],
            'order_items' => [
                ['sku' => 'SKU-1', 'name' => 'Blue mug', 'quantity' => 2, 'price' => 49.95, 'total' => 99.90],
            ],
        ],
    ])
    ->throw()
    ->json('data');

logger()->info($shipment['uuid'].' '.$shipment['tracking_code']);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://app.shipos.co.il/api/v2/shipments",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
        "Idempotency-Key": "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f",
    },
    json={
        "license_key": "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
        "ship_data": {
            "contact_name": "Dana Levi",
            "contact_phone": "0521234567",
            "contact_mail": "dana@example.com",
            "street": "Herzl",
            "number": "10",
            "city": "Tel Aviv",
            "floor": "3",
            "apartment": "12",
            "type": 1,
            "return": 1,
            "packages": 1,
            "note": "Leave with doorman",
        },
        "order": {
            "id": "1042",
            "number": "1042",
            "currency": "ILS",
            "total": 199.90,
            "source": "custom",
            "shipping": {
                "first_name": "Dana",
                "last_name": "Levi",
                "phone": "0521234567",
                "email": "dana@example.com",
                "address_1": "Herzl 10",
                "city": "Tel Aviv",
                "postcode": "6100000",
                "country": "IL",
            },
            "order_items": [
                {"sku": "SKU-1", "name": "Blue mug", "quantity": 2, "price": 49.95, "total": 99.90},
            ],
        },
    },
)
response.raise_for_status()
shipment = response.json()["data"]

print(shipment["uuid"], shipment["tracking_code"])
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"license_key": "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
		"ship_data": map[string]any{
			"contact_name":  "Dana Levi",
			"contact_phone": "0521234567",
			"contact_mail":  "dana@example.com",
			"street":        "Herzl",
			"number":        "10",
			"city":          "Tel Aviv",
			"floor":         "3",
			"apartment":     "12",
			"type":          1,
			"return":        1,
			"packages":      1,
			"note":          "Leave with doorman",
		},
		"order": map[string]any{
			"id":       "1042",
			"number":   "1042",
			"currency": "ILS",
			"total":    199.90,
			"source":   "custom",
			"shipping": map[string]any{
				"first_name": "Dana",
				"last_name":  "Levi",
				"phone":      "0521234567",
				"email":      "dana@example.com",
				"address_1":  "Herzl 10",
				"city":       "Tel Aviv",
				"postcode":   "6100000",
				"country":    "IL",
			},
			"order_items": []map[string]any{
				{"sku": "SKU-1", "name": "Blue mug", "quantity": 2, "price": 49.95, "total": 99.90},
			},
		},
	})

	req, _ := http.NewRequest("POST",
		"https://app.shipos.co.il/api/v2/shipments", bytes.NewReader(body))
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Idempotency-Key", "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(out)) // 201 {"data":{...}}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsCreateShipment {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "license_key": "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
              "ship_data": {
                "contact_name": "Dana Levi",
                "contact_phone": "0521234567",
                "contact_mail": "dana@example.com",
                "street": "Herzl",
                "number": "10",
                "city": "Tel Aviv",
                "floor": "3",
                "apartment": "12",
                "type": 1,
                "return": 1,
                "packages": 1,
                "note": "Leave with doorman"
              },
              "order": {
                "id": "1042",
                "number": "1042",
                "currency": "ILS",
                "total": 199.90,
                "source": "custom",
                "shipping": {
                  "first_name": "Dana",
                  "last_name": "Levi",
                  "phone": "0521234567",
                  "email": "dana@example.com",
                  "address_1": "Herzl 10",
                  "city": "Tel Aviv",
                  "postcode": "6100000",
                  "country": "IL"
                },
                "order_items": [
                  { "sku": "SKU-1", "name": "Blue mug", "quantity": 2, "price": 49.95, "total": 99.90 }
                ]
              }
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/shipments"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .header("Content-Type", "application/json")
            .header("Idempotency-Key", "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 201) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));
http.DefaultRequestHeaders.Add("Idempotency-Key",
    "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f");

var response = await http.PostAsJsonAsync("shipments", new
{
    license_key = "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
    ship_data = new
    {
        contact_name = "Dana Levi",
        contact_phone = "0521234567",
        contact_mail = "dana@example.com",
        street = "Herzl",
        number = "10",
        city = "Tel Aviv",
        floor = "3",
        apartment = "12",
        type = 1,
        @return = 1,
        packages = 1,
        note = "Leave with doorman",
    },
    order = new
    {
        id = "1042",
        number = "1042",
        currency = "ILS",
        total = 199.90,
        source = "custom",
        shipping = new
        {
            first_name = "Dana",
            last_name = "Levi",
            phone = "0521234567",
            email = "dana@example.com",
            address_1 = "Herzl 10",
            city = "Tel Aviv",
            postcode = "6100000",
            country = "IL",
        },
        order_items = new[]
        {
            new { sku = "SKU-1", name = "Blue mug", quantity = 2, price = 49.95, total = 99.90 },
        },
    },
});
response.EnsureSuccessStatusCode();

var payload = await response.Content.ReadFromJsonAsync<JsonDocument>()
    ?? throw new InvalidOperationException("Empty response");
var shipment = payload.RootElement.GetProperty("data");

Console.WriteLine(shipment.GetProperty("uuid").GetString());
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/shipments")

request = Net::HTTP::Post.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f"
request.body = JSON.dump(
  license_key: "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
  ship_data: {
    contact_name: "Dana Levi",
    contact_phone: "0521234567",
    contact_mail: "dana@example.com",
    street: "Herzl",
    number: "10",
    city: "Tel Aviv",
    floor: "3",
    apartment: "12",
    type: 1,
    return: 1,
    packages: 1,
    note: "Leave with doorman"
  },
  order: {
    id: "1042",
    number: "1042",
    currency: "ILS",
    total: 199.90,
    source: "custom",
    shipping: {
      first_name: "Dana",
      last_name: "Levi",
      phone: "0521234567",
      email: "dana@example.com",
      address_1: "Herzl 10",
      city: "Tel Aviv",
      postcode: "6100000",
      country: "IL"
    },
    order_items: [
      { sku: "SKU-1", name: "Blue mug", quantity: 2, price: 49.95, total: 99.90 }
    ]
  }
)

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

shipment = JSON.parse(response.body).fetch("data")
puts "#{shipment["uuid"]} #{shipment["tracking_code"]}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let payload: Value = reqwest::Client::new()
        .post("https://app.shipos.co.il/api/v2/shipments")
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .header("Idempotency-Key", "9c1a5f4e-2b6d-4a3e-8f10-7d0c2b3a4e5f")
        .json(&serde_json::json!({
            "license_key": "b3f1c9a7d2e04f8a9c6b1e2d3f4a5b6c",
            "ship_data": {
                "contact_name": "Dana Levi",
                "contact_phone": "0521234567",
                "contact_mail": "dana@example.com",
                "street": "Herzl",
                "number": "10",
                "city": "Tel Aviv",
                "floor": "3",
                "apartment": "12",
                "type": 1,
                "return": 1,
                "packages": 1,
                "note": "Leave with doorman"
            },
            "order": {
                "id": "1042",
                "number": "1042",
                "currency": "ILS",
                "total": 199.90,
                "source": "custom",
                "shipping": {
                    "first_name": "Dana",
                    "last_name": "Levi",
                    "phone": "0521234567",
                    "email": "dana@example.com",
                    "address_1": "Herzl 10",
                    "city": "Tel Aviv",
                    "postcode": "6100000",
                    "country": "IL"
                },
                "order_items": [
                    { "sku": "SKU-1", "name": "Blue mug", "quantity": 2, "price": 49.95, "total": 99.90 }
                ]
            }
        }))
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    println!("{} {}", payload["data"]["uuid"], payload["data"]["tracking_code"]);
    Ok(())
}

Response 201

json
{
  "data": {
    "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "tracking_code": "66747921",
    "carrier": { "id": 3, "name": "HFD" },
    "status": null,
    "service_type": "1",
    "is_active": true,
    "recipient": {
      "name": "Dana Levi",
      "phone": "0521234567",
      "company": null,
      "address": {
        "street": "Herzl 10",
        "number": "10",
        "city": "Tel Aviv",
        "state": null,
        "zip": "6100000",
        "country": "IL"
      }
    },
    "pickup_point_id": null,
    "packages": 1,
    "cod": null,
    "order": { "id": "1042", "number": "1042" },
    "references": { "external_id": null },
    "short_tracking_code": null,
    "label_generated": false,
    "collection_status": null,
    "collected_at": null,
    "ready_at": null,
    "created_at": "2026-07-29T09:14:00.000000Z",
    "updated_at": "2026-07-29T09:14:00.000000Z"
  }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenlicense_key not owned by the caller, or the resolved license is inactive/expired, or the account has no active license.
403package_limit_reachedThe customer's subscription shipment quota (max_no_of_shipping) is exhausted.
409duplicate_requestA create for the same request is already in flight (lock timeout). Retry shortly.
409idempotency_key_conflictThe same Idempotency-Key was reused with a different request body. See Idempotency.
422validation_failedBody failed validation, or the account has more than one license and license_key was omitted.
424carrier_errorThe carrier rejected the shipment.

The shipment object

Every shipment endpoint returns this shape. Fields are curated for the public contract; internal carrier payloads and operational columns are not exposed.

FieldTypeDescription
uuidstringPublic shipment id.
tracking_codestring | nullCarrier tracking code.
carrierobjectThe carrier: { id, name }.
statusobject | null{ code, description, is_delivered } — null-valued keys dropped; whole field null until a status is known.
service_typestring | null1 = delivery, 2 = collection/return.
is_activebooleanWhether the shipment is active (set false on cancel).
recipientobject{ name, phone, company, address: { street, number, city, state, zip, country } }.
pickup_point_idstring | nullPickup-point id, for pickup-point deliveries.
packagesinteger | nullPackage count.
codobject | null{ amount } when cash-on-delivery, otherwise null.
orderobject{ id, number } — the neutral order reference.
referencesobject{ external_id }.
short_tracking_codestring | nullShort tracking code (pickup-point flows).
label_generatedbooleanWhether a label has been generated.
collection_statusmixed | nullCollection lifecycle status.
collected_atdatetime | nullWhen collected.
ready_atdatetime | nullWhen ready for collection.
created_atdatetimeCreation timestamp.
updated_atdatetimeLast update timestamp.

GET /shipments/

Retrieve a single shipment. Resolved across every license you own, so no license_key is needed. Auth: client credentials. License: optional.

Parameters

Path:

FieldTypeRequiredDescription
shipmentstringYesThe shipment uuid, the carrier tracking code, or the short tracking code.

Query:

FieldTypeRequiredDescription
license_keystringNolicenses.key to restrict the lookup to one carrier account. Omitted, it spans every license you own.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments/9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88'

const response = await fetch(`https://app.shipos.co.il/api/v2/shipments/${uuid}`, {
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    Accept: 'application/json',
  },
})

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: shipment } = await response.json()
console.log(shipment.tracking_code, shipment.status?.description)
php
<?php
// composer require guzzlehttp/guzzle

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$shipment = json_decode(
    $client->get("shipments/{$uuid}")->getBody()->getContents(),
    true,
)['data'];

echo $shipment['tracking_code'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$shipment = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get("https://app.shipos.co.il/api/v2/shipments/{$uuid}")
    ->throw()
    ->json('data');

logger()->info($shipment['tracking_code']);
python
# pip install httpx
import os

import httpx

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

response = httpx.get(
    f"https://app.shipos.co.il/api/v2/shipments/{shipment}",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()
shipment = response.json()["data"]

print(shipment["tracking_code"])
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	uuid := "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/shipments/"+uuid, nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(body)) // {"data":{...}}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsGetShipment {
    public static void main(String[] args) throws Exception {
        String uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/shipments/" + uuid))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

var uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>($"shipments/{shipment}")
    ?? throw new InvalidOperationException("Empty response");
var shipment = payload.RootElement.GetProperty("data");

Console.WriteLine(shipment.GetProperty("tracking_code").GetString());
ruby
require "net/http"
require "json"

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"
uri = URI("https://app.shipos.co.il/api/v2/shipments/#{uuid}")

request = Net::HTTP::Get.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

shipment = JSON.parse(response.body).fetch("data")
puts shipment["tracking_code"]
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

    let payload: Value = reqwest::Client::new()
        .get(format!("https://app.shipos.co.il/api/v2/shipments/{shipment}"))
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    println!("{}", payload["data"]["tracking_code"]);
    Ok(())
}

Response 200

json
{ "data": { "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88", "tracking_code": "66747921", "...": "see the shipment object above" } }

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenLicense not owned / inactive / expired, or no active license.
404not_foundNo shipment with that uuid under the resolved license.
422validation_failedMultiple licenses and license_key omitted.

GET /shipments/{shipment}/status

Re-fetch the shipment's status live from the carrier and return the updated shipment. If the live lookup fails, the last-known stored status is kept. Auth: client credentials. License: optional.

Parameters

Path:

FieldTypeRequiredDescription
shipmentstringYesThe shipment uuid, the carrier tracking code, or the short tracking code.

Query:

FieldTypeRequiredDescription
license_keystringNolicenses.key to restrict the lookup to one carrier account. Omitted, it spans every license you own.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments/9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88/status' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88'

const response = await fetch(
  `https://app.shipos.co.il/api/v2/shipments/${uuid}/status`,
  {
    headers: {
      'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
      'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
      Accept: 'application/json',
    },
  },
)

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: shipment } = await response.json()
console.log(shipment.status?.description, shipment.status?.is_delivered)
php
<?php
// composer require guzzlehttp/guzzle

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$shipment = json_decode(
    $client->get("shipments/{$uuid}/status")->getBody()->getContents(),
    true,
)['data'];

echo $shipment['status']['description'] ?? 'unknown', PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$shipment = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get("https://app.shipos.co.il/api/v2/shipments/{$uuid}/status")
    ->throw()
    ->json('data');

logger()->info($shipment['status']['description'] ?? 'unknown');
python
# pip install httpx
import os

import httpx

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

response = httpx.get(
    f"https://app.shipos.co.il/api/v2/shipments/{shipment}/status",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()
status = response.json()["data"]["status"] or {}

print(status.get("description"), status.get("is_delivered"))
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	uuid := "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/shipments/"+uuid+"/status", nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(body)) // {"data":{"status":{...},...}}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsShipmentStatus {
    public static void main(String[] args) throws Exception {
        String uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(
                "https://app.shipos.co.il/api/v2/shipments/" + uuid + "/status"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{"status":{...},...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

var uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>($"shipments/{shipment}/status")
    ?? throw new InvalidOperationException("Empty response");
var status = payload.RootElement.GetProperty("data").GetProperty("status");

Console.WriteLine(status.ValueKind == JsonValueKind.Null
    ? "unknown"
    : status.GetProperty("description").GetString());
ruby
require "net/http"
require "json"

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"
uri = URI("https://app.shipos.co.il/api/v2/shipments/#{uuid}/status")

request = Net::HTTP::Get.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

status = JSON.parse(response.body).dig("data", "status") || {}
puts "#{status["description"]} delivered=#{status["is_delivered"]}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

    let payload: Value = reqwest::Client::new()
        .get(format!(
            "https://app.shipos.co.il/api/v2/shipments/{shipment}/status"
        ))
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    let status = &payload["data"]["status"];
    println!("{} {}", status["description"], status["is_delivered"]);
    Ok(())
}

Response 200

Returns the full shipment object with a refreshed status block.

json
{
  "data": {
    "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "tracking_code": "66747921",
    "status": { "code": "5", "description": "Delivered", "is_delivered": true },
    "...": "remaining shipment fields"
  }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenLicense not owned / inactive / expired, or no active license.
404not_foundNo shipment with that uuid under the resolved license.
422validation_failedMultiple licenses and license_key omitted.

GET /shipments/{shipment}/label

Return the printable-label metadata and download URL for a single shipment. Auth: client credentials. License: optional.

This endpoint does not return or redirect to the binary itself. It responds with a JSON envelope containing the shipment id, the format (pdf), and a url pointing at the label download route (shipping.label). Fetch that URL to obtain the PDF.

Parameters

Path:

FieldTypeRequiredDescription
shipmentstringYesThe shipment uuid, the carrier tracking code, or the short tracking code.

Query:

FieldTypeRequiredDescription
license_keystringNolicenses.key to restrict the lookup to one carrier account. Omitted, it spans every license you own.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments/9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88/label' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / browsers — no dependencies
const uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88'

const response = await fetch(
  `https://app.shipos.co.il/api/v2/shipments/${uuid}/label`,
  {
    headers: {
      'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
      'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
      Accept: 'application/json',
    },
  },
)

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: label } = await response.json()
console.log(label.format, label.url) // fetch label.url for the PDF
php
<?php
// composer require guzzlehttp/guzzle

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$label = json_decode(
    $client->get("shipments/{$uuid}/label")->getBody()->getContents(),
    true,
)['data'];

echo $label['format'], ' → ', $label['url'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$label = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->get("https://app.shipos.co.il/api/v2/shipments/{$uuid}/label")
    ->throw()
    ->json('data');

logger()->info($label['format'].' → '.$label['url']);
python
# pip install httpx
import os

import httpx

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

response = httpx.get(
    f"https://app.shipos.co.il/api/v2/shipments/{shipment}/label",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()
label = response.json()["data"]

print(label["format"], label["url"])  # fetch label["url"] for the PDF
go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

type labelResponse struct {
	Data struct {
		Format string `json:"format"`
		URL    string `json:"url"`
	} `json:"data"`
}

func main() {
	uuid := "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/shipments/"+uuid+"/label", nil)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var label labelResponse
	if err := json.NewDecoder(res.Body).Decode(&label); err != nil {
		panic(err)
	}

	fmt.Println(label.Data.Format, label.Data.URL)
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsShipmentLabel {
    public static void main(String[] args) throws Exception {
        String uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(
                "https://app.shipos.co.il/api/v2/shipments/" + uuid + "/label"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{"format":"pdf","url":"..."}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

var uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var payload = await http.GetFromJsonAsync<JsonDocument>($"shipments/{shipment}/label")
    ?? throw new InvalidOperationException("Empty response");
var label = payload.RootElement.GetProperty("data");

Console.WriteLine($"{label.GetProperty("format").GetString()} → " +
    label.GetProperty("url").GetString());
ruby
require "net/http"
require "json"

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"
uri = URI("https://app.shipos.co.il/api/v2/shipments/#{uuid}/label")

request = Net::HTTP::Get.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

label = JSON.parse(response.body).fetch("data")
puts "#{label["format"]}#{label["url"]}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

    let payload: Value = reqwest::Client::new()
        .get(format!(
            "https://app.shipos.co.il/api/v2/shipments/{shipment}/label"
        ))
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    let label = &payload["data"];
    println!("{} → {}", label["format"], label["url"]);
    Ok(())
}

Response 200

json
{
  "data": {
    "shipment_id": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "format": "pdf",
    "url": "https://app.shipos.co.il/shipping/label/9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"
  }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenLicense not owned / inactive / expired, or no active license.
404not_foundNo shipment with that uuid under the resolved license.
422validation_failedMultiple licenses and license_key omitted.

POST /shipments/labels

Return a single combined-PDF label URL covering many shipments at once. The uuids may be uuids or tracking codes. Auth: client credentials. License: optional.

Only the caller's own shipments are included; unknown or foreign uuids are silently dropped. As with the single-label endpoint, the response is JSON metadata with a url (the shipping.bulk_label route) — not the binary.

Parameters

Body:

FieldTypeRequiredDescription
license_keystringNolicenses.key to restrict the lookup to one carrier account. Omitted, it spans every license you own.
uuidsarrayYesShipment uuids to combine. Min 1, max 100. De-duplicated server-side.
uuids.*stringYesA shipment uuid.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments/labels' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
  "uuids": [
    "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12"
  ]
}'
js
// Node.js 18+ / browsers — no dependencies
const response = await fetch('https://app.shipos.co.il/api/v2/shipments/labels', {
  method: 'POST',
  headers: {
    'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
    'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    uuids: [
      '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88',
      '1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12',
    ],
  }),
})

if (!response.ok) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: labels } = await response.json()
console.log(labels.count, labels.url) // combined PDF for all labels
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

$labels = json_decode($client->post('shipments/labels', [
    'json' => [
        'uuids' => [
            '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88',
            '1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12',
        ],
    ],
])->getBody()->getContents(), true)['data'];

echo $labels['count'], ' → ', $labels['url'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$labels = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->post('https://app.shipos.co.il/api/v2/shipments/labels', [
        'uuids' => [
            '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88',
            '1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12',
        ],
    ])
    ->throw()
    ->json('data');

logger()->info($labels['count'].' → '.$labels['url']);
python
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://app.shipos.co.il/api/v2/shipments/labels",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
    json={
        "uuids": [
            "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
            "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12",
        ],
    },
)
response.raise_for_status()
labels = response.json()["data"]

print(labels["count"], labels["url"])
go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"uuids": []string{
			"9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
			"1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12",
		},
	})

	req, _ := http.NewRequest("POST",
		"https://app.shipos.co.il/api/v2/shipments/labels", bytes.NewReader(body))
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(out)) // {"data":{"count":2,"url":"..."}}
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsBulkLabels {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "uuids": [
                "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
                "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12"
              ]
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/shipments/labels"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{"count":2,"url":"..."}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net.Http.Json;
using System.Text.Json;

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

var response = await http.PostAsJsonAsync("shipments/labels", new
{
    uuids = new[]
    {
        "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
        "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12",
    },
});
response.EnsureSuccessStatusCode();

var payload = await response.Content.ReadFromJsonAsync<JsonDocument>()
    ?? throw new InvalidOperationException("Empty response");
var labels = payload.RootElement.GetProperty("data");

Console.WriteLine($"{labels.GetProperty("count").GetInt32()} → " +
    labels.GetProperty("url").GetString());
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/shipments/labels")

request = Net::HTTP::Post.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
request.body = JSON.dump(uuids: [
  "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
  "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12"
])

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

raise "ShipOS error: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

labels = JSON.parse(response.body).fetch("data")
puts "#{labels["count"]}#{labels["url"]}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let payload: Value = reqwest::Client::new()
        .post("https://app.shipos.co.il/api/v2/shipments/labels")
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        .json(&serde_json::json!({
            "uuids": [
                "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
                "1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12"
            ]
        }))
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

    let labels = &payload["data"];
    println!("{} → {}", labels["count"], labels["url"]);
    Ok(())
}

Response 200

json
{
  "data": {
    "count": 2,
    "format": "pdf",
    "url": "https://app.shipos.co.il/shipping/bulk-label?ids[]=9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88&ids[]=1d5e8a2c-9f0b-4e11-8c33-6a7b8c9d0e12"
  }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenLicense not owned / inactive / expired, or no active license.
404not_foundNone of the supplied uuids resolve to a shipment under the resolved license.
422validation_faileduuids missing/empty/over 100, or multiple licenses and license_key omitted.

POST /shipments/{shipment}/cancel

Cancel a shipment with the carrier. On carrier success the shipment is deactivated locally (is_active becomes false) and a shipment.cancelled webhook is fired. The shipment already knows its carrier, so no license_key is needed. Auth: client credentials. License: optional.

The response status reflects the carrier outcome: 200 when cancelled, 424 when the carrier rejected the cancellation. In both cases the body carries the same shape.

Parameters

Path:

FieldTypeRequiredDescription
shipmentstringYesThe shipment uuid, the carrier tracking code, or the short tracking code.

Body:

FieldTypeRequiredDescription
license_keystringNolicenses.key to restrict the lookup to one carrier account. Omitted, it spans every license you own.

Example request

bash
curl --location 'https://app.shipos.co.il/api/v2/shipments/9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88/cancel' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json'
js
// Node.js 18+ / browsers — no dependencies
const uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88'

const response = await fetch(
  `https://app.shipos.co.il/api/v2/shipments/${uuid}/cancel`,
  {
    method: 'POST',
    headers: {
      'X-Client-Id': process.env.SHIPOS_CLIENT_ID,
      'X-Client-Secret': process.env.SHIPOS_CLIENT_SECRET,
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    // add { license_key } when the account has more than one license
    body: JSON.stringify({}),
  },
)

// 424 is a carrier refusal, not an error envelope
if (!response.ok && response.status !== 424) {
  const { error } = await response.json()
  throw new Error(`${error.code}: ${error.message}`)
}

const { data: result } = await response.json()
console.log(result.cancelled, result.message)
php
<?php
// composer require guzzlehttp/guzzle

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.shipos.co.il/api/v2/',
    'headers' => [
        'X-Client-Id' => getenv('SHIPOS_CLIENT_ID'),
        'X-Client-Secret' => getenv('SHIPOS_CLIENT_SECRET'),
        'Accept' => 'application/json',
    ],
]);

// 424 is a carrier refusal carrying the same data shape
$response = $client->post("shipments/{$uuid}/cancel", [
    'json' => new \stdClass(), // add ['license_key' => '...'] with multiple licenses
    'http_errors' => false,
]);

$result = json_decode($response->getBody()->getContents(), true)['data'];

echo var_export($result['cancelled'], true), ' ', $result['message'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$uuid = '9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88';

$response = Http::withHeaders([
    'X-Client-Id' => config('services.shipos.client_id'),
    'X-Client-Secret' => config('services.shipos.client_secret'),
])
    ->acceptJson()
    ->post("https://app.shipos.co.il/api/v2/shipments/{$uuid}/cancel", [
        // 'license_key' => '...' when the account has more than one license
    ]);

// 424 is a carrier refusal carrying the same data shape
$result = $response->throwIf($response->status() !== 424)->json('data');

logger()->info($result['message'], ['cancelled' => $result['cancelled']]);
python
# pip install httpx
import os

import httpx

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

response = httpx.post(
    f"https://app.shipos.co.il/api/v2/shipments/{shipment}/cancel",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
    json={},  # add {"license_key": "..."} with multiple licenses
)
# 424 is a carrier refusal carrying the same data shape
if response.status_code != 424:
    response.raise_for_status()
result = response.json()["data"]

print(result["cancelled"], result["message"])
go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	uuid := "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"

	// add {"license_key":"..."} when the account has more than one license
	body := bytes.NewReader([]byte(`{}`))

	req, _ := http.NewRequest("POST",
		"https://app.shipos.co.il/api/v2/shipments/"+uuid+"/cancel", body)
	req.Header.Set("X-Client-Id", os.Getenv("SHIPOS_CLIENT_ID"))
	req.Header.Set("X-Client-Secret", os.Getenv("SHIPOS_CLIENT_SECRET"))
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := io.ReadAll(res.Body)
	// 200 = cancelled, 424 = carrier refused (same data shape)
	fmt.Println(res.StatusCode, string(out))
}
java
// Java 17+ — java.net.http, no dependencies (parse with Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsCancelShipment {
    public static void main(String[] args) throws Exception {
        String uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(
                "https://app.shipos.co.il/api/v2/shipments/" + uuid + "/cancel"))
            .header("X-Client-Id", System.getenv("SHIPOS_CLIENT_ID"))
            .header("X-Client-Secret", System.getenv("SHIPOS_CLIENT_SECRET"))
            .header("Accept", "application/json")
            .header("Content-Type", "application/json")
            // add {"license_key":"..."} with more than one license
            .POST(HttpRequest.BodyPublishers.ofString("{}"))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        // 424 is a carrier refusal carrying the same data shape
        if (response.statusCode() != 200 && response.statusCode() != 424) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{"cancelled":true,...}}
    }
}
csharp
// .NET 8+ — System.Net.Http.Json
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;

var uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

using var http = new HttpClient
{
    BaseAddress = new Uri("https://app.shipos.co.il/api/v2/"),
};
http.DefaultRequestHeaders.Add("X-Client-Id",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_ID"));
http.DefaultRequestHeaders.Add("X-Client-Secret",
    Environment.GetEnvironmentVariable("SHIPOS_CLIENT_SECRET"));

// add license_key to the body when the account has more than one license
var response = await http.PostAsJsonAsync($"shipments/{shipment}/cancel", new { });

// 424 is a carrier refusal carrying the same data shape
if (response.StatusCode != HttpStatusCode.FailedDependency)
{
    response.EnsureSuccessStatusCode();
}

var payload = await response.Content.ReadFromJsonAsync<JsonDocument>()
    ?? throw new InvalidOperationException("Empty response");
var result = payload.RootElement.GetProperty("data");

Console.WriteLine($"{result.GetProperty("cancelled").GetBoolean()} " +
    result.GetProperty("message").GetString());
ruby
require "net/http"
require "json"

uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88"
uri = URI("https://app.shipos.co.il/api/v2/shipments/#{uuid}/cancel")

request = Net::HTTP::Post.new(uri)
request["X-Client-Id"] = ENV.fetch("SHIPOS_CLIENT_ID")
request["X-Client-Secret"] = ENV.fetch("SHIPOS_CLIENT_SECRET")
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
request.body = JSON.dump({}) # add license_key: "..." with multiple licenses

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

# 424 is a carrier refusal carrying the same data shape
unless response.is_a?(Net::HTTPSuccess) || response.code == "424"
  raise "ShipOS error: #{response.body}"
end

result = JSON.parse(response.body).fetch("data")
puts "#{result["cancelled"]} #{result["message"]}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let uuid = "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88";

    let response = reqwest::Client::new()
        .post(format!(
            "https://app.shipos.co.il/api/v2/shipments/{shipment}/cancel"
        ))
        .header("X-Client-Id", std::env::var("SHIPOS_CLIENT_ID")?)
        .header("X-Client-Secret", std::env::var("SHIPOS_CLIENT_SECRET")?)
        .header("Accept", "application/json")
        // add "license_key" when the account has more than one license
        .json(&serde_json::json!({}))
        .send()
        .await?;

    // 424 is a carrier refusal carrying the same data shape
    let status = response.status();
    let payload: Value = response.json().await?;
    if !status.is_success() && status.as_u16() != 424 {
        return Err(format!("ShipOS error: {payload}").into());
    }

    println!("{} {}", payload["data"]["cancelled"], payload["data"]["message"]);
    Ok(())
}

Response 200

json
{
  "data": {
    "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "cancelled": true,
    "message": "Shipment cancelled."
  }
}

When the carrier rejects the cancellation, the same body is returned with cancelled: false and a 424 status:

json
{
  "data": {
    "uuid": "9b2c7f1e-3a4d-4c88-9f21-2f0d5a6b7c88",
    "cancelled": false,
    "message": "The carrier could not cancel this shipment."
  }
}

Errors

StatuscodeWhen
401unauthenticatedMissing or invalid client credentials.
403forbiddenLicense not owned / inactive / expired, or no active license.
404not_foundNo shipment with that uuid under the resolved license.
422validation_failedMultiple licenses and license_key omitted.

Note: a carrier-rejected cancellation is not an error-envelope response — it returns the data shape above with HTTP 424 and cancelled: false.