Skip to content

אצוות (Batches)

אצוות הן יצירת משלוחים בכמות גדולה באופן אסינכרוני. אתם שולחים עד 2000 payloads של משלוחים בבקשה אחת; ה-API שומר אותם, מתזמן את העבודה על-פני workers מקביליים, ומחזיר מיד 202 Accepted עם אצווה שאפשר לבצע עליה polling. כל פריט נוצר דרך אותו נתיב מוגן cache+lock כמו POST /shipments בודד, כך שפריט פגום נכשל בפני עצמו במקום לדחות את כל האצווה.

כל ה-endpoints של אצוות תחומים לרישיון: בוחרים את חשבון חברת השילוח באמצעות license_key (ב-query ב-GET, בגוף בקריאת היצירה) = הערך של licenses.key. רישיון פעיל יחיד משמש כברירת מחדל; מספר רישיונות פעילים ללא license_key מחזיר 422; ללא רישיון / לא פעיל / פג תוקף / לא בבעלות מחזיר 403.

מחזור חיים

אצווה עוברת בין הסטטוסים הבאים:

סטטוסמשמעות
queuedנוצרה; משימות ה-chunk שוגרו, אף פריט טרם הוכרע.
processingלפחות פריט אחד הוכרע, אך לא כולם.
completedכל פריט הוכרע (כל אחד הוא created או failed).

לכל פריט סטטוס משלו: pending (טרם עובד), created (המשלוח נוצר — ראו shipment_id), או failed (ראו error). בצעו polling ל-GET /batches/{uuid} לספירות מצטברות ול-GET /batches/{uuid}/items לתוצאות פר-פריט.


GET /batches

הצגת רשימת האצוות של הקורא, מהחדשה לישנה, כל אחת עם הפריטים שלה טעונים מראש (eager-loaded). עימוד מבוסס-cursor. אימות: client credentials. רישיון: חובה.

פרמטרים

Query

שדהסוגחובהתיאור
license_keystringחובה*ה-licenses.key הבוחר את חשבון חברת השילוח. אופציונלי כאשר לקורא יש בדיוק רישיון פעיל אחד.
per_pageintegerלאפריטים לעמוד. ברירת מחדל 25, מוגבל ל-100.

דוגמת בקשה

bash
curl --location 'https://app.shipos.co.il/api/v2/batches?license_key={license_key}&per_page=25' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / דפדפנים — ללא תלויות
const url = new URL('https://app.shipos.co.il/api/v2/batches')
url.searchParams.set('license_key', '{license_key}')
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: batches } = await response.json()

for (const batch of batches) {
  console.log(batch.id, batch.status, batch.summary.created, 'created')
}
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',
    ],
]);

$response = $client->get('batches', [
    'query' => ['license_key' => '{license_key}', 'per_page' => 25],
]);

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

foreach ($batches as $batch) {
    echo $batch['id'], ' ', $batch['status'], ' ', $batch['summary']['created'], PHP_EOL;
}
php
<?php

use Illuminate\Support\Facades\Http;

$batches = 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/batches', [
        'license_key' => '{license_key}',
        'per_page' => 25,
    ])
    ->throw()
    ->json('data');

foreach ($batches as $batch) {
    logger()->info($batch['id'].' '.$batch['status'].' '.$batch['summary']['created']);
}
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/batches",
    params={"license_key": "{license_key}", "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()

for batch in response.json()["data"]:
    print(batch["id"], batch["status"], batch["summary"]["created"])
go
package main

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

type batchList struct {
	Data []struct {
		ID      string `json:"id"`
		Status  string `json:"status"`
		Summary struct {
			Created int `json:"created"`
			Failed  int `json:"failed"`
		} `json:"summary"`
	} `json:"data"`
}

func main() {
	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/batches?license_key={license_key}&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()

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

	for _, batch := range list.Data {
		fmt.Println(batch.ID, batch.Status, batch.Summary.Created, batch.Summary.Failed)
	}
}
java
// Java 17+ — java.net.http, ללא תלויות (פענוח עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsListBatches {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/batches"
                + "?license_key={license_key}&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":[...]} — מפו עם Jackson/Gson
    }
}
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>(
    "batches?license_key={license_key}&per_page=25")
    ?? throw new InvalidOperationException("Empty response");

foreach (var batch in payload.RootElement.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{batch.GetProperty("id").GetString()} " +
        $"{batch.GetProperty("status").GetString()}");
}
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/batches")
uri.query = URI.encode_www_form(license_key: "{license_key}", 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)

JSON.parse(response.body).fetch("data").each do |batch|
  puts "#{batch["id"]} #{batch["status"]} #{batch.dig("summary", "created")}"
end
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/batches")
        .query(&[("license_key", "{license_key}"), ("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?;

    for batch in payload["data"].as_array().unwrap_or(&Vec::new()) {
        println!("{} {} {}", batch["id"], batch["status"], batch["summary"]["created"]);
    }
    Ok(())
}

תשובה 200

כל אצווה כוללת את מערך ה-items שלה (נוכח רק ב-endpoint האינדקס הזה, שבו הרלציה נטענת מראש). ראו את מבנה הפריט תחת GET /batches/{uuid}/items.

json
{
  "data": [
    {
      "id": "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55",
      "status": "completed",
      "summary": {
        "total": 2,
        "created": 1,
        "failed": 1,
        "pending": 0
      },
      "items": [
        {
          "id": 4101,
          "position": 0,
          "status": "created",
          "shipment_id": "b7d3a1e0-9f2c-4a8b-8e11-6c5d4b3a2f10",
          "payload": { "...": "הגוף שנשלח עבור הפריט" },
          "error": null,
          "created_at": "2026-07-29T08:00:00.000000Z",
          "updated_at": "2026-07-29T08:00:04.000000Z"
        }
      ],
      "created_at": "2026-07-29T08:00:00.000000Z",
      "updated_at": "2026-07-29T08:00:06.000000Z"
    }
  ],
  "links": { "...": "קישורי עימוד cursor" },
  "meta": { "...": "מטא-נתוני עימוד cursor" }
}

שגיאות

סטטוסקודמתי
403forbiddenאין רישיון שמיש (לא פעיל / פג תוקף / לא בבעלות).
422validation_failedמספר רישיונות פעילים וללא license_key.

POST /batches

תזמון אצוות יצירת משלוחים אסינכרונית. אימות: client credentials. רישיון: חובה.

מחזיר 202 Accepted — המשלוחים עדיין לא נוצרו. בצעו polling על ה-id של האצווה שהוחזר.

פרמטרים

Body

הבקשה מקבלת כל אחד משני מבני המעטפת, שניהם שקולים:

  • { "shipments": [ <item>, <item>, ... ] }, או
  • מערך JSON חשוף ברמה העליונה [ <item>, <item>, ... ] (מנורמל תחת shipments).
שדהסוגחובהתיאור
shipmentsarrayכן1 עד 2000 payloads של משלוחים.
license_keystringחובה*רישיון ברירת המחדל לכל פריט. שדה בגוף. אופציונלי כאשר לקורא יש בדיוק רישיון פעיל אחד.

כל פריט (shipments[]) הוא גוף משלוח מלא — אותו payload שמתקבל ב-POST /shipments — ומאומת פר-פריט במהלך העיבוד (פריטים לא תקינים הופכים ל-failed, הם אינם דוחים את האצווה). בנוסף:

שדה פריטסוגחובהתיאור
license_keystringלאדריסה פר-פריט הבוחרת רישיון אחר (חייב להשתייך לאותו לקוח, אחרת הפריט נכשל עם forbidden). נופל חזרה לרישיון ברירת המחדל של האצווה כשהוא מושמט.

דוגמת בקשה

bash
curl --location 'https://app.shipos.co.il/api/v2/batches' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
  "license_key": "{license_key}",
  "shipments": [
    { "ship_data": { "...": "shipment fields" }, "order": { "...": "order fields" } },
    { "ship_data": { "...": "shipment fields" }, "order": { "...": "order fields" }, "license_key": "{other_license_key}" }
  ]
}'
js
// Node.js 18+ / דפדפנים — ללא תלויות
const item = {
  ship_data: { /* shipment fields — same as POST /shipments */ },
  order: { /* order fields */ },
}

const response = await fetch('https://app.shipos.co.il/api/v2/batches', {
  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({
    license_key: '{license_key}',
    shipments: [item, { ...item, license_key: '{other_license_key}' }],
  }),
})

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

const { data: batch } = await response.json() // 202 Accepted

console.log(batch.id, batch.status, batch.summary.pending)
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',
    ],
]);

// כל פריט הוא גוף מלא של POST /shipments.
$item = ['ship_data' => [/* shipment fields */], 'order' => [/* order fields */]];

$response = $client->post('batches', [
    'json' => [
        'license_key' => '{license_key}',
        'shipments' => [
            $item,
            $item + ['license_key' => '{other_license_key}'],
        ],
    ],
]);

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

echo $batch['id'], ' ', $batch['status'], ' pending: ', $batch['summary']['pending'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

// כל פריט הוא גוף מלא של POST /shipments.
$item = ['ship_data' => [/* shipment fields */], 'order' => [/* order fields */]];

$batch = 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/batches', [
        'license_key' => '{license_key}',
        'shipments' => [$item, $item + ['license_key' => '{other_license_key}']],
    ])
    ->throw()
    ->json('data');

logger()->info("batch {$batch['id']} is {$batch['status']}");
python
# pip install httpx
import os

import httpx

item = {"ship_data": {}, "order": {}}  # גוף מלא של POST /shipments

response = httpx.post(
    "https://app.shipos.co.il/api/v2/batches",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
    json={
        "license_key": "{license_key}",
        "shipments": [item, {**item, "license_key": "{other_license_key}"}],
    },
)
response.raise_for_status()
batch = response.json()["data"]  # 202 Accepted

print(batch["id"], batch["status"], batch["summary"]["pending"])
go
package main

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

func main() {
	// כל פריט הוא גוף מלא של POST /shipments.
	item := map[string]any{"ship_data": map[string]any{}, "order": map[string]any{}}
	override := map[string]any{"ship_data": map[string]any{}, "order": map[string]any{},
		"license_key": "{other_license_key}"}

	body, _ := json.Marshal(map[string]any{
		"license_key": "{license_key}",
		"shipments":   []any{item, override},
	})

	req, _ := http.NewRequest("POST", "https://app.shipos.co.il/api/v2/batches",
		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)) // 202 {"data":{"status":"queued",...}}
}
java
// Java 17+ — java.net.http, ללא תלויות (בניית JSON עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsCreateBatch {
    public static void main(String[] args) throws Exception {
        // כל פריט הוא גוף מלא של POST /shipments.
        String body = """
            {
              "license_key": "{license_key}",
              "shipments": [
                {"ship_data": {}, "order": {}},
                {"ship_data": {}, "order": {}, "license_key": "{other_license_key}"}
              ]
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/batches"))
            .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() != 202) {
            throw new RuntimeException("ShipOS error: " + response.body());
        }

        System.out.println(response.body()); // {"data":{"status":"queued",...}}
    }
}
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"));

// כל פריט הוא גוף מלא של POST /shipments.
var response = await http.PostAsJsonAsync("batches", new
{
    license_key = "{license_key}",
    shipments = new object[]
    {
        new { ship_data = new { }, order = new { } },
        new { ship_data = new { }, order = new { }, license_key = "{other_license_key}" },
    },
});
response.EnsureSuccessStatusCode(); // 202 Accepted

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

Console.WriteLine($"{batch.GetProperty("id").GetString()} " +
    $"{batch.GetProperty("status").GetString()}");
ruby
require "net/http"
require "json"

item = { ship_data: {}, order: {} } # גוף מלא של POST /shipments

uri = URI("https://app.shipos.co.il/api/v2/batches")
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({
  license_key: "{license_key}",
  shipments: [item, item.merge(license_key: "{other_license_key}")],
})

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)

batch = JSON.parse(response.body).fetch("data") # 202 Accepted
puts "#{batch["id"]} #{batch["status"]} #{batch.dig("summary", "pending")}"
rust
// [dependencies]
// reqwest = { version = "0.12", features = ["json"] }
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let item = json!({ "ship_data": {}, "order": {} }); // גוף מלא של POST /shipments

    let payload: Value = reqwest::Client::new()
        .post("https://app.shipos.co.il/api/v2/batches")
        .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(&json!({
            "license_key": "{license_key}",
            "shipments": [
                item,
                { "ship_data": {}, "order": {}, "license_key": "{other_license_key}" }
            ],
        }))
        .send()
        .await?
        .error_for_status()?
        .json()
        .await?;

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

תשובה 202

json
{
  "data": {
    "id": "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55",
    "status": "queued",
    "summary": {
      "total": 2,
      "created": 0,
      "failed": 0,
      "pending": 2
    },
    "created_at": "2026-07-29T08:00:00.000000Z",
    "updated_at": "2026-07-29T08:00:00.000000Z"
  }
}

items מושמט כאן (הרלציה נטענת מראש רק ב-endpoint האינדקס).

שגיאות

סטטוסקודמתי
403forbiddenאין רישיון שמיש (לא פעיל / פג תוקף / לא בבעלות).
422validation_failedshipments חסר/ריק, יותר מ-2000 פריטים, או מספר רישיונות פעילים וללא license_key.

GET /batches/

החזרת אצווה בודדת בבעלות הרישיון של הקורא — ה-endpoint הראשי ל-polling של התקדמות מצטברת. אימות: client credentials. רישיון: חובה.

פרמטרים

Path

שדהסוגחובהתיאור
uuidstringכןה-id של האצווה (UUID) שהוחזר ביצירה.

Query

שדהסוגחובהתיאור
license_keystringחובה*בוחר את חשבון חברת השילוח. אופציונלי עם רישיון פעיל יחיד.

דוגמת בקשה

bash
curl --location 'https://app.shipos.co.il/api/v2/batches/9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55?license_key={license_key}' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / דפדפנים — ללא תלויות
const batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55'
const url = new URL(`https://app.shipos.co.il/api/v2/batches/${batchId}`)
url.searchParams.set('license_key', '{license_key}')

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: batch } = await response.json()

// בצעו polling עד ש-batch.status === 'completed'.
console.log(batch.status, batch.summary.created, batch.summary.pending)
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',
    ],
]);

$batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55';

$response = $client->get("batches/{$batchId}", [
    'query' => ['license_key' => '{license_key}'],
]);

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

// בצעו polling עד ש-$batch['status'] === 'completed'.
echo $batch['status'], ' ', $batch['summary']['created'], '/', $batch['summary']['total'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55';

$batch = 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/batches/{$batchId}", [
        'license_key' => '{license_key}',
    ])
    ->throw()
    ->json('data');

// בצעו polling עד ש-$batch['status'] === 'completed'.
logger()->info($batch['status'], $batch['summary']);
python
# pip install httpx
import os

import httpx

batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

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

# בצעו polling עד ש-batch["status"] == "completed".
print(batch["status"], batch["summary"])
go
package main

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

type batchResponse struct {
	Data struct {
		ID      string `json:"id"`
		Status  string `json:"status"`
		Summary struct {
			Total   int `json:"total"`
			Created int `json:"created"`
			Failed  int `json:"failed"`
			Pending int `json:"pending"`
		} `json:"summary"`
	} `json:"data"`
}

func main() {
	batchID := "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/batches/"+batchID+"?license_key={license_key}", 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 batch batchResponse
	if err := json.NewDecoder(res.Body).Decode(&batch); err != nil {
		panic(err)
	}

	// בצעו polling עד ש-batch.Data.Status == "completed".
	fmt.Println(batch.Data.Status, batch.Data.Summary.Created, batch.Data.Summary.Pending)
}
java
// Java 17+ — java.net.http, ללא תלויות (פענוח עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsShowBatch {
    public static void main(String[] args) throws Exception {
        String batchId = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/batches/" + batchId
                + "?license_key={license_key}"))
            .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());
        }

        // בצעו polling עד ש-data.status הוא "completed".
        System.out.println(response.body()); // {"data":{...}} — מפו עם Jackson/Gson
    }
}
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 batchId = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

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

// בצעו polling עד ש-status הוא "completed".
Console.WriteLine($"{batch.GetProperty("status").GetString()} " +
    $"{batch.GetProperty("summary").GetProperty("pending").GetInt32()} pending");
ruby
require "net/http"
require "json"

batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

uri = URI("https://app.shipos.co.il/api/v2/batches/#{batch_id}")
uri.query = URI.encode_www_form(license_key: "{license_key}")

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)

batch = JSON.parse(response.body).fetch("data")

# בצעו polling עד ש-batch["status"] == "completed".
puts "#{batch["status"]} #{batch["summary"]}"
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 batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

    let payload: Value = reqwest::Client::new()
        .get(format!("https://app.shipos.co.il/api/v2/batches/{batch_id}"))
        .query(&[("license_key", "{license_key}")])
        .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 batch = &payload["data"];
    // בצעו polling עד ש-batch["status"] == "completed".
    println!("{} {}", batch["status"], batch["summary"]);
    Ok(())
}

תשובה 200

json
{
  "data": {
    "id": "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55",
    "status": "processing",
    "summary": {
      "total": 2,
      "created": 1,
      "failed": 0,
      "pending": 1
    },
    "created_at": "2026-07-29T08:00:00.000000Z",
    "updated_at": "2026-07-29T08:00:04.000000Z"
  }
}

שדות התשובה

שדהסוגתיאור
idstringה-UUID של האצווה.
statusstringqueued,‏ processing או completed.
summary.totalintegerסך הפריטים שנשלחו.
summary.createdintegerפריטים שהניבו משלוח.
summary.failedintegerפריטים שנכשלו.
summary.pendingintegerפריטים שטרם הוכרעו (total - created - failed, לא פחות מ-0).
itemsarrayנוכח רק ב-endpoint האינדקס; מושמט כאן.
created_at / updated_atstringחותמות זמן ISO-8601.

שגיאות

סטטוסקודמתי
403forbiddenאין רישיון שמיש.
404not_foundאף אצווה עם ה-UUID הזה אינה שייכת לרישיון של הקורא (Batch not found.).
422validation_failedמספר רישיונות פעילים וללא license_key.

GET /batches/{uuid}/items

החזרת התוצאות פר-פריט של אצווה, עם עימוד. אימות: client credentials. רישיון: חובה.

פרמטרים

Path

שדהסוגחובהתיאור
uuidstringכןה-id של האצווה (UUID).

Query

שדהסוגחובהתיאור
license_keystringחובה*בוחר את חשבון חברת השילוח. אופציונלי עם רישיון פעיל יחיד.
per_pageintegerלאפריטים לעמוד. ברירת מחדל 50, מוגבל ל-100.

דוגמת בקשה

bash
curl --location 'https://app.shipos.co.il/api/v2/batches/9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55/items?license_key={license_key}&per_page=50' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / דפדפנים — ללא תלויות
const batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55'
const url = new URL(`https://app.shipos.co.il/api/v2/batches/${batchId}/items`)
url.searchParams.set('license_key', '{license_key}')
url.searchParams.set('per_page', '50')

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: items } = await response.json()

for (const item of items) {
  console.log(item.position, item.status, item.shipment_id ?? item.error?.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',
    ],
]);

$batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55';

$response = $client->get("batches/{$batchId}/items", [
    'query' => ['license_key' => '{license_key}', 'per_page' => 50],
]);

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

foreach ($items as $item) {
    echo $item['position'], ' ', $item['status'], ' ',
        $item['shipment_id'] ?? ($item['error']['code'] ?? ''), PHP_EOL;
}
php
<?php

use Illuminate\Support\Facades\Http;

$batchId = '9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55';

$items = 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/batches/{$batchId}/items", [
        'license_key' => '{license_key}',
        'per_page' => 50,
    ])
    ->throw()
    ->json('data');

foreach ($items as $item) {
    logger()->info($item['position'].' '.$item['status'], ['error' => $item['error']]);
}
python
# pip install httpx
import os

import httpx

batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

response = httpx.get(
    f"https://app.shipos.co.il/api/v2/batches/{batch_id}/items",
    params={"license_key": "{license_key}", "per_page": 50},
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()

for item in response.json()["data"]:
    print(item["position"], item["status"], item["shipment_id"] or item["error"])
go
package main

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

type itemList struct {
	Data []struct {
		Position   int             `json:"position"`
		Status     string          `json:"status"`
		ShipmentID *string         `json:"shipment_id"`
		Error      json.RawMessage `json:"error"`
	} `json:"data"`
}

func main() {
	batchID := "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

	req, _ := http.NewRequest("GET",
		"https://app.shipos.co.il/api/v2/batches/"+batchID+
			"/items?license_key={license_key}&per_page=50", 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 list itemList
	if err := json.NewDecoder(res.Body).Decode(&list); err != nil {
		panic(err)
	}

	for _, item := range list.Data {
		fmt.Println(item.Position, item.Status, string(item.Error))
	}
}
java
// Java 17+ — java.net.http, ללא תלויות (פענוח עם Jackson/Gson)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsBatchItems {
    public static void main(String[] args) throws Exception {
        String batchId = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/batches/" + batchId
                + "/items?license_key={license_key}&per_page=50"))
            .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":[...]} — מפו עם Jackson/Gson
    }
}
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 batchId = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

var payload = await http.GetFromJsonAsync<JsonDocument>(
    $"batches/{batchId}/items?license_key={{license_key}}&per_page=50")
    ?? throw new InvalidOperationException("Empty response");

foreach (var item in payload.RootElement.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{item.GetProperty("position").GetInt32()} " +
        $"{item.GetProperty("status").GetString()} {item.GetProperty("error")}");
}
ruby
require "net/http"
require "json"

batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55"

uri = URI("https://app.shipos.co.il/api/v2/batches/#{batch_id}/items")
uri.query = URI.encode_www_form(license_key: "{license_key}", per_page: 50)

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)

JSON.parse(response.body).fetch("data").each do |item|
  puts "#{item["position"]} #{item["status"]} #{item["shipment_id"] || item["error"]}"
end
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 batch_id = "9b1f7c2e-3a4d-4f18-9c7a-2b6e1d0f8a55";

    let payload: Value = reqwest::Client::new()
        .get(format!("https://app.shipos.co.il/api/v2/batches/{batch_id}/items"))
        .query(&[("license_key", "{license_key}"), ("per_page", "50")])
        .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?;

    for item in payload["data"].as_array().unwrap_or(&Vec::new()) {
        println!("{} {} {}", item["position"], item["status"], item["error"]);
    }
    Ok(())
}

תשובה 200

json
{
  "data": [
    {
      "id": 4101,
      "position": 0,
      "status": "created",
      "shipment_id": "b7d3a1e0-9f2c-4a8b-8e11-6c5d4b3a2f10",
      "payload": { "...": "הגוף שנשלח עבור הפריט" },
      "error": null,
      "created_at": "2026-07-29T08:00:00.000000Z",
      "updated_at": "2026-07-29T08:00:04.000000Z"
    },
    {
      "id": 4102,
      "position": 1,
      "status": "failed",
      "shipment_id": null,
      "payload": { "...": "הגוף שנשלח עבור הפריט" },
      "error": {
        "code": "validation_failed",
        "ship_data.city": ["The ship_data.city field is required."]
      },
      "created_at": "2026-07-29T08:00:00.000000Z",
      "updated_at": "2026-07-29T08:00:05.000000Z"
    }
  ],
  "links": { "...": "קישורי עימוד cursor" },
  "meta": { "...": "מטא-נתוני עימוד cursor" }
}

שדות התשובה (פר פריט)

שדהסוגתיאור
idintegerמזהה פריט האצווה.
positionintegerאינדקס מבוסס-אפס בתוך האצווה שנשלחה.
statusstringpending,‏ created או failed.
shipment_idstring | nullה-UUID של המשלוח שנוצר כאשר status הוא created; אחרת null.
payloadobjectהגוף המדויק שנשלח עבור הפריט.
errorobject | nullפירוט הכשל כאשר status הוא failed: תמיד code (למשל validation_failed,‏ forbidden,‏ carrier_error,‏ server_error) בתוספת message ו/או שגיאות ברמת השדה. אחרת null.
created_at / updated_atstringחותמות זמן ISO-8601.

שגיאות

סטטוסקודמתי
403forbiddenאין רישיון שמיש.
404not_foundאף אצווה עם ה-UUID הזה אינה שייכת לרישיון של הקורא (Batch not found.).
422validation_failedמספר רישיונות פעילים וללא license_key.