적재 API

화물을 제출하면 물리적으로 유효한 최적화된 적재 계획(위치, 적재 미터, 피트 단위 길이, 청구 팔레트 위치)을 반환합니다. 앱의 핵심 엔진과 동일하며, 실제 디스패처 레이아웃으로 지속 검증됩니다. 모든 플랜에서 샌드박스 이용 가능, 엔터프라이즈 플랜에서 프로덕션 이용 가능.

빠른 시작

  1. 로그인 → 이름 클릭 → API → 키 생성.
  2. 적재 작업을 제출합니다(이 작업은 샌드박스에서 실행되며 모든 플랜에서 무료로 이용 가능):
curl -X POST https://hansatic.com/api/v1/pack \
  -H "Authorization: Bearer hk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vehicle": "CURTAINSIDER_136",
    "objective": "ldm",
    "sandbox": true,
    "items": [{
      "name": "Machine crate",
      "length": 2300, "width": 750, "height": 1400,
      "weight": 300, "quantity": 7,
      "stackable": false, "tiltable": false
    }]
  }'
{
  "job_id": "5e3617d1-abe4-4549-b7e6-d18c6baf814f",
  "status": "queued",
  "sandbox": true,
  "status_url": "https://hansatic.com/api/v1/jobs/5e3617d1-..."
}

status_url을(를) 폴링합니다(동일한 인증 헤더 사용). status이(가) "done"일 때까지 계속 확인하세요.

{
  "job_id": "5e3617d1-...",
  "status": "done",
  "result": {
    "placed_count": 7,
    "unplaced_count": 0,
    "units": "metric",
    "metrics": {
      "ldm_m": 5.25,
      "span_mm": 5250,
      "linear_ft": 17.2,
      "pallet_positions_eur": 14,
      "total_weight_kg": 2100,
      "overlaps": 0
    },
    "items": [
      { "id": "...", "name": "Machine crate", "placed": true,
        "x": 8370, "y": 0, "z": 0,
        "rotation_deg": 90, "tilt_deg": 0, "...": "..." }
    ],
    "plan_url": "https://hansatic.com/en/app/plans/..."
  }
}

엔드포인트

POST /api/v1/pack

적재 작업을 검증하고 대기열에 추가합니다. 202job_id를 반환합니다. Idempotency-Key 헤더를 지원하며, 동일한 키로 재시도 시 원래 작업이 반환되어 중복 적재를 방지합니다.

GET /api/v1/jobs/{id}

작업 상태 및 완료 시 전체 결과를 제공합니다. 결과는 30일간 보관되며, 프로덕션 플랜의 결과는 영구적으로 워크스페이스에 저장됩니다.

GET /api/v1/openapi.json

기계가 읽을 수 있는 OpenAPI 3.1 스펙 — Postman, 코드 생성기 또는 AI 코딩 도구에 가져올 수 있습니다. 차량 코드는 실시간 엔진에서 주입되므로 항상 최신 상태입니다. 스펙 보기 ↗

응답 필드

완료된 작업의 result 객체:

필드유형설명
placed_count / unplaced_countinteger적재된 항목 수 / 적재되지 않은 항목 수.
units"metric" | "imperial"이하 모든 숫자의 단위(요청과 동일).
metrics.ldm_mnumber적재 미터 — 화물 블록의 길이(최대 끝 지점 − 최소 시작 지점).
metrics.linear_ftnumber동일한 길이의 피트 단위(미국 LTL).
metrics.span_mmintegermm 단위의 길이(항상 미터법 사용).
metrics.pallet_positions_eurinteger | null청구 EUR 팔레트 위치. 레이아웃이 너무 커서 측정할 수 없을 경우 null.
metrics.total_weight_kgnumber적재된 총 중량(항상 kg).
metrics.overlapsinteger겹치는 항목 쌍 — 유효한 레이아웃에서는 항상 0.
items[].placedboolean항목이 레이아웃에 포함되었는지 여부.
items[].x / y / znumber | null요청 단위의 위치: x는 문 쪽 끝에서, y는 왼쪽 벽에서, z는 바닥에서. placed가 false일 경우 세 값 모두 null.
items[].rotation_deg / tilt_deg0 | 90방향: 수직 축을 기준으로 회전, tilt는 옆으로 눕힘.
items[].length / width / height / weightnumber제출된 항목의 치수(요청 단위 반영).
plan_urlstring | null앱에서 생성된 계획 링크. 샌드박스 작업의 경우 null.

버전 관리 및 안정성

계약서는 경로(/api/v1/)에 버전이 명시됩니다. v1 내에서 변경 사항은 추가적인 요소만 포함됩니다 — 새로운 선택적 요청 필드, 응답 필드, 차량 코드, 오류 코드가 추가될 수 있으며, 클라이언트는 인식하지 못하는 필드는 무시해야 합니다. 기존 필드의 의미나 유형은 변경되지 않으며, 삭제도 없습니다. 호환성이 깨지는 변경 사항은 새로운 버전 경로로 제공되며, v1은 최소 12개월 동안 병행 유지되며 활성 키가 있는 모든 워크스페이스에 이메일로 폐기 통지가 발송됩니다.

요청 필드

필드유형설명
vehiclestring아래 표의 차량 코드 또는 {customVehicleId}(워크스페이스 차량 목록 중 하나)를 사용하세요.
objective"ldm" | "pallets"적재 미터 최소화 또는 청구 팔레트 위치 최소화. 기본값은 "ldm".
units"metric" | "imperial"항목 치수와 중량의 단위 및 반환 방식: mm/kg(기본값) 또는 in/lb. 응답의 위치도 동일한 단위를 따릅니다.
itemsarray, required화물 목록(아래 필드 참조).
sandboxboolean테스트 모드: 전체 결과 제공, 저장되지 않음. 모든 플랜에서 이용 가능.
namestring계획 이름(프로덕션 모드).
ldm_limit_mnumber적재 길이를 처음 N 적재 미터로 제한(트럭).
budget_msnumber최적화 시간 예산(프로덕션, 기본값 30000, 최대 300000).
callback_urlhttps URL작업 완료 시 결과가 POST로 전송되며 서명됩니다(웹훅 참조).

항목 필드

필드유형설명
length / width / heightnumber, required항목 치수(요청의 단위 기준, 기본값 mm, "units": "imperial"일 경우 인치).
weightnumber, required항목 중량(기본값 kg, "units": "imperial"일 경우 lb).
quantityinteger, default 1개별 단위로 확장됩니다.
namestring, optional계획 및 응답에 표시됩니다.
stackableboolean, default true이 항목 위에 다른 화물을 쌓을 수 있습니다.
max_stacknumber, optional이 항목 위에 쌓을 수 있는 최대 중량(weight와 동일한 단위).
fragileboolean, default false깨지기 쉬운 항목은 최상층에만 배치됩니다.
tiltableboolean, default true항목을 옆으로 눕힐 수 있습니다.
destination_stopinteger, default 1다중 정차 LIFO 적재 시 배송 정류장.

차량 코드

실시간 엔진 사양에서 생성됩니다. 워크스페이스의 사용자 지정 차량은 custom_vehicle_id로 사용 가능합니다.

트럭 및 트레일러

코드차량내부 치수 (L×W×H)적재 중량
CURTAINSIDER_13613.6m Curtainsider13.62 × 2.48 × 2.70 m24 t
BOX_13613.6m Box Trailer13.62 × 2.46 × 2.68 m24 t
MEGA_13613.6m Mega Trailer13.62 × 2.48 × 3.00 m22.5 t
MEGA_CURTAIN_13613.6m Mega Curtainsider13.62 × 2.48 × 3.00 m22 t
FLATBED_13613.6m Flatbed13.62 × 2.48 × 3.00 m25 t
REEFER_13613.6m Reefer13.32 × 2.44 × 2.54 m22 t
CURTAINSIDER_757.5m City Curtainsider7.50 × 2.48 × 2.70 m12 t
SWAPBODY_7157.15m Swap Body7.15 × 2.48 × 2.70 m12 t
SWAPBODY_7457.45m Swap Body7.45 × 2.48 × 2.70 m12 t
DRY_VAN_5353ft Dry Van16.15 × 2.59 × 2.74 m20 t
FLATBED_5353ft Flatbed16.15 × 2.59 × 3.66 m22 t
REEFER_5353ft Reefer15.85 × 2.46 × 2.59 m18.5 t
CURTAINSIDER_125_CN12.5m Curtainsider (CN)12.50 × 2.50 × 2.80 m27 t
FLATBED_40_IN40ft Flatbed (IN)12.19 × 2.50 × 3.50 m26 t
BOX_138_BR13.8m Box Van (BR)13.80 × 2.50 × 2.90 m28 t
CURTAINSIDER_12_ZA12m Curtainsider (ZA)12.00 × 2.48 × 2.70 m28 t

컨테이너

코드컨테이너내부 치수 (L×W×H)적재 중량
20DC20' Dry Container5.89 × 2.35 × 2.39 m21.727 t
20HC20' High Cube5.89 × 2.35 × 2.70 m21.58 t
40DC40' Dry Container12.03 × 2.35 × 2.39 m26.63 t
40HC40' High Cube12.03 × 2.35 × 2.70 m26.58 t
45HC45' High Cube13.56 × 2.35 × 2.70 m25.68 t
20OT20' Open Top5.89 × 2.35 × 2.39 m21.64 t
40OT40' Open Top12.03 × 2.35 × 2.39 m26.58 t
20RF20' Reefer5.44 × 2.29 × 2.27 m21 t
40RF40' Reefer11.58 × 2.29 × 2.21 m25.6 t
40RFHC40' Reefer High Cube11.58 × 2.29 × 2.54 m25.88 t
53DC53' Domestic16.15 × 2.59 × 2.90 m26 t

접근 수준 및 제한

샌드박스(모든 플랜)프로덕션(엔터프라이즈)
호출월 25회 성공 호출월 5,000건 성공 처리
목록당 항목 수205,000
동시 작업110
최적화 예산10초기본 30초, 최대 300초
영속성저장되지 않음모든 적재 작업이 워크스페이스에 실제 계획으로 생성됨

성공적으로 완료된 패킹 작업만 카운트되며, 유효성 검사 실패 또는 작업 실패 건은 할당량에 포함되지 않습니다. 두 카운터는 매월 1일(UTC 기준) 초기화됩니다. 샌드박스 결과는 완전하며 기능 제한이 없습니다 — 제한은 접근 제어일 뿐, 결과물에는 영향을 주지 않습니다. 월 5,000건 이상의 패킹이 필요한 경우, 대량 및 OEM 가격에 대해 문의해 주시기 바랍니다.

웹훅

작업 완료 시 결과를 POST로 수신하려면 callback_url을(를) 전달하세요. 모든 콜백에는 X-Hansatic-Signature가 포함됩니다: 원시 본문에 대한 HMAC-SHA256 서명(웹훅 비밀키는 키 생성 시 한 번 표시됨). 서명을 확인하세요:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, webhookSecret) {
  const expected = crypto.createHmac('sha256', webhookSecret)
    .update(rawBody).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(signatureHeader));
}

전송은 5초 타임아웃으로 한 번 시도되며, status_url이(가) 항상 진실 공급원입니다.

오류

모든 오류는 { "error": { "code", "message", ... } } 형식으로 제공됩니다 — 기계가 읽을 수 있는 코드, 사람이 읽을 수 있는 메시지, 특정 입력 오류 시 field 포함.

코드HTTP의미
invalid_key401누락, 잘못된 형식 또는 취소된 API 키입니다.
invalid_json400본문이 유효한 JSON이 아닙니다.
invalid_vehicle400알 수 없는 차량 코드 또는 {customVehicleId}입니다.
invalid_items400항목 필드가 누락되었거나 숫자가 아니거나 범위를 벗어났습니다.
suspicious_dimensions400모든 치수가 100mm 미만입니다 — 인치를 mm로 전송했을 가능성이 있습니다. "units": "imperial"을(를) 전달하세요.
invalid_units400units는 "metric" 또는 "imperial"이어야 합니다.
invalid_objective400objective는 "ldm" 또는 "pallets"이어야 합니다.
invalid_callback400callback_url은 https여야 합니다.
too_many_items400목록이 접근 수준의 항목 제한을 초과했습니다.
upgrade_required402프로덕션 모드는 엔터프라이즈 플랜이 필요합니다. 샌드박스를 사용하거나 플랜을 업그레이드하세요.
sandbox_limit_reached402월간 샌드박스 허용량을 모두 사용했습니다 — 매월 1일에 초기화됩니다.
quota_exceeded402월간 생산 할당량 5,000건을 모두 사용했습니다 — 매월 1일에 초기화됩니다. 더 많은 용량이 필요하면 문의해 주시기 바랍니다.
too_many_jobs429동시 작업 제한에 도달했습니다. 실행 중인 작업을 폴링하고 재시도하세요.
not_found404이 워크스페이스에 해당 작업이 없습니다.

질문, 더 높은 제한, 또는 필요한 커넥터(Zapier, Excel, MCP)가 있으신가요? hello@hansatic.com