Skip to content

אימות

ה-ShipOS v2 API מאמת קוראים באמצעות פרטי לקוח APIX-Client-Id ו-X-Client-Secret — הנשלחים ככותרות HTTP בכל בקשה ל-endpoint מאומת. רק GET /ping ציבורי; כל שאר ה-endpoints, כולל מעקב, דורשים פרטי גישה.

איך זה עובד

שלחו את שתי כותרות פרטי הגישה, בתוספת Accept: application/json, בכל בקשה מאומתת:

Headerחובהתיאור
X-Client-Idכןמזהה הלקוח הציבורי של החשבון.
X-Client-Secretכןהסוד (secret) של החשבון. מושווה בצד השרת בזמן קבוע (hash_equals).
Acceptמומלץשלחו תמיד application/json.

ה-X-Client-Id מזהה את חשבון הסוחר (User). החשבון מוצמד לבקשה כדי שה-endpoint יפעל תחתיו. אין החלפת token ואין session — כל בקשה נושאת את פרטי הגישה ישירות.

מאיפה מגיעים פרטי הגישה

פרטי הגישה הם לכל חשבון סוחר. הם נוצרים ומחודשים על ידי מנהל מערכת של ShipOS מעמוד עריכת הלקוח בפאנל הניהול של ShipOS. אין endpoint ב-v2 API ליצירה, החלפה או שליפה של פרטי גישה — זו פעולה שמתבצעת בפאנל הניהול בלבד.

מקרי כישלון

שתי השגיאות משתמשות בקוד unauthenticated וב-HTTP 401, עטופות במעטפת השגיאה הסטנדרטית:

תנאיStatuscodemessage
אחת הכותרות חסרה401unauthenticatedMissing API credentials. Send X-Client-Id and X-Client-Secret.
X-Client-Id לא מוכר, או X-Client-Secret שגוי401unauthenticatedInvalid API client credentials.
json
{
  "error": {
    "code": "unauthenticated",
    "message": "Invalid API client credentials.",
    "status": 401
  }
}

דוגמה מלאה

שליפת החשבון הקורא עם GET /account:

bash
curl --location 'https://app.shipos.co.il/api/v2/account' \
--header 'X-Client-Id: {client_id}' \
--header 'X-Client-Secret: {client_secret}' \
--header 'Accept: application/json'
js
// Node.js 18+ / דפדפנים — בלי תלויות
const response = await fetch('https://app.shipos.co.il/api/v2/account', {
  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}`) // unauthenticated ב-401
}

const { data: account } = await response.json()

console.log('Authenticated as', account.company_name)
php
<?php
// composer require guzzlehttp/guzzle

$client = new \GuzzleHttp\Client();

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

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

echo 'Authenticated as ', $account['company_name'], PHP_EOL;
php
<?php

use Illuminate\Support\Facades\Http;

$account = 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/account')
    ->throw()
    ->json('data');

logger()->info('Authenticated as '.$account['company_name']);
python
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://app.shipos.co.il/api/v2/account",
    headers={
        "X-Client-Id": os.environ["SHIPOS_CLIENT_ID"],
        "X-Client-Secret": os.environ["SHIPOS_CLIENT_SECRET"],
        "Accept": "application/json",
    },
)
response.raise_for_status()  # זורק שגיאה ב-401 unauthenticated

print("Authenticated as", response.json()["data"]["company_name"])
go
package main

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

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

	if res.StatusCode != http.StatusOK {
		panic(fmt.Sprintf("ShipOS error: HTTP %d", res.StatusCode))
	}

	var payload struct {
		Data struct {
			CompanyName string `json:"company_name"`
		} `json:"data"`
	}
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		panic(err)
	}

	fmt.Println("Authenticated as", payload.Data.CompanyName)
}
java
// Java 17+ — java.net.http, בלי תלויות
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class ShipOsCredentialCheck {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://app.shipos.co.il/api/v2/account"))
            .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;

using var http = new HttpClient();
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("Accept", "application/json");

var payload = await http.GetFromJsonAsync<JsonDocument>(
        "https://app.shipos.co.il/api/v2/account")
    ?? throw new InvalidOperationException("Empty response");

var account = payload.RootElement.GetProperty("data");

Console.WriteLine($"Authenticated as {account.GetProperty("company_name").GetString()}");
ruby
require "net/http"
require "json"

uri = URI("https://app.shipos.co.il/api/v2/account")
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)

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

puts "Authenticated as #{account["company_name"]}"
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/account")
        .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!("Authenticated as {}", payload["data"]["company_name"]);
    Ok(())
}

קריאה מוצלחת מחזירה את חשבון הסוחר עטוף ב-{ "data": { ... } }. ראו GET /account למבנה התגובה המלא.