openapi: 3.0.3 info: title: Lingxing WMS Internal Read API version: 1.0.0 description: >- Single-page or single-order adapter. No scheduling, business persistence or public Gateway route. Source times remain uninterpreted strings; warehouse timeZone does not certify source timezone. Server-side collection mode defaults to OFF. VALIDATION requires an externally confirmed exclusive account window; BUSINESS requires environmentLabel exactly production or test. TEST business task creation is restricted by Lastleg to controlled commands and their legitimate derived work; the adapter does not schedule work. Provider-query endpoints apply the same gate. Missing exclusive proof, persistent account budget authorization or control context fails closed before egress. At most one account request is in flight; request starts obey the bound minStartInterval (VALIDATION at least 5 seconds). A JVM lock is only local protection and is not proof of cross-environment account exclusivity. Each invocation performs at most one supplier HTTP attempt. No mode uses implicit HTTP retries; 429 is never retried, immediately pauses the account and returns persistent cooldown metadata. VALIDATION page size must not exceed 10. Lastleg owns root/session budgets and cooldown persistence. External server configuration supplies externalExclusiveGrantId and a VALIDATION externalExclusiveUntil; the nonempty grant must match context.exclusiveGrantId. BUSINESS has no grant, session or root time cutoff; explicit stops, persistent account protections and each short single-attempt permit remain enforced. A BUSINESS rate-limit pause is recoverable after both the persistently returned cooldown and the local minimum fallback cooldown have elapsed, and after the authority issues a new strictly valid single-attempt permit confirming account budget, cooldown, stop limits and root state. Time alone never permits egress. VALIDATION 429 does not automatically recover. Unavailable, lost or malformed authorization and uncertain completion hold an unrecoverable local pause until operations verify persistent account state. Completion failure never automatically recovers. A well-formed explicit authorization denial is deterministic and does not cause a permanent pause. sourceAccount defaults to lingxing-primary and budgetAuthorityBaseUri defaults internally to http://lastleg-service:9303; neither default enables egress. x-lastleg-egress-coordination: description: >- Integration calls these Lastleg endpoints with X-Internal-Service-Token. These are dependencies, not Integration Controller paths. Lastleg prepares attemptId server-side and binds context and operation before authorization. authorize only consumes an already prepared attempt, first binds the opaque requestHash and atomically consumes persistent account/root/session budget. Duplicate authorization is refused; arbitrary client-created attempts are refused. complete verifies the same bindings. A permit is bound to its full context, operation and requestHash, requires exclusiveConfirmed=true and budgetConsumed=true, expires after now but no later than now plus 30 seconds (also within externalExclusiveUntil for VALIDATION), and cannot be reused. Explicit denial uses HTTP 200 with ApiResponse code string B0001 and AuthorizationDenial data. A valid denial is not an uncertain authorization and does not impose a permanent local pause. Unavailable, lost or malformed authorization or an unconfirmed callback blocks further egress; pending or ambiguous attempts remain conservatively consumed, including after restart. authorize: method: POST path: /internal/lastleg/self-operated-analytics/egress/authorize requestSchema: '#/components/schemas/AuthorizationRequest' responseSchema: '#/components/schemas/AttemptPermitResponse' complete: method: POST path: /internal/lastleg/self-operated-analytics/egress/complete requestSchema: '#/components/schemas/CompletionRequest' responseSchema: '#/components/schemas/CompletionResponse' security: - InternalServiceToken: [] paths: /internal/integrations/lingxing/wms/egress/runtime: get: operationId: lingxingEgressRuntime summary: Read sanitized configuration from the active egress gate description: >- Internal token authentication only. No provider, database, budget-authority, permit or persistence side effect. Returns actual bound configuration, not a verified claim or a commit identity. Fingerprints use lowercase SHA-256 of UTF-8 "selfop-runtime:v1:\n" plus the exact original nonblank value, without trimming. Domains are source-account, grant and budget-authority. Missing/blank values yield null, never an empty-value hash. security: [{InternalServiceToken: []}] responses: '200': description: ApiResponse code 200 with runtime snapshot, or A0401 for missing/invalid internal token content: application/json: schema: type: object properties: code: {type: string} data: {$ref: '#/components/schemas/EgressRuntimeSnapshot'} /internal/integrations/lingxing/wms/warehouses/query: post: summary: Query one page of normal warehouses parameters: - {$ref: '#/components/parameters/LingxingEgressContext'} description: Calls /openapi/v2/warehouse/page without filters. Maps page/pageSize into current/size; preserves original metadata. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/WarehouseQueryRequest'} responses: '200': description: Standard envelope with WarehousePage or sanitized ProviderFailure. content: application/json: schema: {$ref: '#/components/schemas/WarehouseResponse'} /internal/integrations/lingxing/wms/outbound-orders/query: post: summary: Query one page of completed outbound orders by source outbound time parameters: - {$ref: '#/components/parameters/LingxingEgressContext'} description: Calls /openapi/v2/order/delivery/page with timeType=outboundTime and status=100. No external warehouse filter. At current=1,size=100 only, records=[],total=0,pages=0,sourceCurrent=1,sourceSize=10 is accepted and internal size remains 100. All other pagination metadata is strictly required. No completeness claim across pages. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/QueryRequest'} responses: '200': description: Standard envelope with OrderPage or sanitized ProviderFailure. content: application/json: schema: {$ref: '#/components/schemas/OrderResponse'} /internal/integrations/lingxing/wms/outbound-orders/locate: post: summary: Locate one page by delivery number without time or status filters parameters: - {$ref: '#/components/parameters/LingxingEgressContext'} description: Sends only current,size,deliveryNo to delivery/page. Every returned deliveryNo must match exactly. Caller reads all required pages and verifies original customer identity and uniqueness before accepting the current warehouse. Missing results do not prove cancellation. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/LocateRequest'} responses: '200': description: Standard envelope with OrderPage or sanitized ProviderFailure. content: application/json: schema: {$ref: '#/components/schemas/OrderResponse'} /internal/integrations/lingxing/wms/outbound-orders/detail: post: summary: Read one order using warehouse, customer and delivery identifiers parameters: - {$ref: '#/components/parameters/LingxingEgressContext'} description: Maps warehouseCode to whCode for delivery/detail. All three returned identifiers must match. Returns only skuVOList; packageList is never added to quantities. Duplicate SKU lines remain separate for caller normalization. Missing quantities remain null; no quantity unit is inferred. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/DetailRequest'} responses: '200': description: Standard envelope with OrderDetail or sanitized ProviderFailure. content: application/json: schema: {$ref: '#/components/schemas/DetailResponse'} /internal/integrations/lingxing/wms/product-inventory/query: post: summary: Query one page of all qualities and customers in a required warehouse parameters: - {$ref: '#/components/parameters/LingxingEgressContext'} description: Calls stock/stockSku/page with whCode and isHideInventory=0. No SKU, quality, customer or quantity filters. Warehouse mismatch and duplicate business keys within a page fail closed. Caller validates cross-page keys and counts. Unknown quantities stay null, negative quantities remain evidence, stockCompare is not accumulated. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/InventoryQueryRequest'} responses: '200': description: Standard envelope with InventoryPage or sanitized ProviderFailure. content: application/json: schema: {$ref: '#/components/schemas/InventoryResponse'} components: parameters: LingxingEgressContext: name: X-Lingxing-Egress-Context in: header required: true description: >- JSON control context; no credentials. Maximum 2048 characters, all printable ASCII (32 through 126). Unknown fields, duplicate fields and trailing JSON are rejected. Identifiers are strings supplied by the server-side coordinator and do not confer authorization by themselves. content: application/json: schema: {$ref: '#/components/schemas/EgressContext'} securitySchemes: InternalServiceToken: type: apiKey in: header name: X-Internal-Service-Token schemas: EgressRuntimeSnapshot: type: object additionalProperties: false required: [schemaVersion, service, observedAt, mode, environment, continuousEnabled, grantPresent, intervalMs, permitMaxSeconds, providerEnabled] properties: schemaVersion: {type: integer, enum: [1]} service: {type: string, enum: [integration]} observedAt: {type: string, format: date-time} mode: {type: string, enum: [OFF, VALIDATION, BUSINESS]} environment: {type: string} continuousEnabled: {type: boolean} sourceAccountFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'} grantPresent: {type: boolean} grantFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'} intervalMs: oneOf: [{type: integer, format: int64}, {type: string, pattern: '^[0-9]+$'}] description: Actual bound interval; shared long-value JSON serialization may encode it as a decimal string. permitMaxSeconds: {type: integer, enum: [30]} providerEnabled: {type: boolean} budgetAuthorityFingerprint: {type: string, nullable: true, pattern: '^[a-f0-9]{64}$'} EgressContext: type: object additionalProperties: false required: [sourceAccount, executionPurpose, rootTaskId, sessionId, attemptId, exclusiveGrantId] properties: sourceAccount: {type: string, minLength: 1, maxLength: 64, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: 'Server-configured account alias, never App Key or Secret.'} executionPurpose: {type: string, enum: [VALIDATION, BUSINESS]} rootTaskId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Stable root control identifier; child tasks share root budget.} sessionId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Stable session control identifier; reopening or restart does not reset it.} attemptId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Prepared unique single-attempt control identifier; duplicate authorization is refused.} exclusiveGrantId: {type: string, minLength: 1, maxLength: 128, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]*$', description: Must equal the valid externalExclusiveGrantId configured server-side.} EgressRequestHash: type: string pattern: '^[0-9a-f]{64}$' description: >- Lowercase hexadecimal SHA256 of UTF-8 bytes of OMP path, one LF character and compact insertion-ordered external business JSON. Lastleg treats this digest as opaque and binds it on the first authorization of an already prepared attempt. example: fa60a92c78b4803878930e5914e6db03591acc31b7c09abad53c91912fa3d627 x-preimage-example: |- /openapi/v2/stock/stockSku/page {"current":1,"size":2,"whCode":"WH-DEMO","isHideInventory":0} EgressOperation: type: string enum: [WAREHOUSES, OUTBOUND_QUERY, OUTBOUND_LOCATE, OUTBOUND_DETAIL, PRODUCT_INVENTORY] EgressMetadata: type: object required: [attemptId, actualHttpAttempts, accountPaused] properties: attemptId: {type: string, nullable: true, description: Null only if no valid control context was accepted.} actualHttpAttempts: {type: integer, minimum: 0, maximum: 1, description: 'Supplier HTTP attempts actually started, including failed calls; authorization callbacks do not count.'} accountPaused: {type: boolean} cooldownUntil: {type: string, format: date-time, nullable: true, description: Account cooldown deadline to persist; valid supplier retry time must not be shortened.} AuthorizationRequest: type: object required: [context, operation, requestHash] properties: context: {$ref: '#/components/schemas/EgressContext'} operation: {$ref: '#/components/schemas/EgressOperation'} requestHash: {$ref: '#/components/schemas/EgressRequestHash'} AuthorizationDenial: type: object required: [errorCode, sampleDone] properties: errorCode: {type: string, pattern: '^[A-Z][A-Z0-9_]{0,63}$'} sampleDone: {type: boolean, description: Strict JSON boolean; validation budget normal completion is distinct from a failure.} waitUntil: {type: string, format: date-time, nullable: true, description: Nullable ISO-8601 Instant from the persistent authority.} AuthorizationDenialResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: code: {type: string, enum: [B0001]} data: {$ref: '#/components/schemas/AuthorizationDenial'} AttemptPermit: type: object required: [context, operation, requestHash, allowedAttempts, expiresAt, exclusiveConfirmed, budgetConsumed] properties: context: {$ref: '#/components/schemas/EgressContext'} operation: {$ref: '#/components/schemas/EgressOperation'} requestHash: {$ref: '#/components/schemas/EgressRequestHash'} allowedAttempts: {type: integer, enum: [1]} expiresAt: {type: string, format: date-time, description: Must be after now and no later than now plus 30 seconds and configured externalExclusiveUntil.} exclusiveConfirmed: {type: boolean, enum: [true]} budgetConsumed: {type: boolean, enum: [true], description: Lastleg has atomically consumed persistent account and root/session budget before returning this permit.} CompletionRequest: type: object required: [context, operation, requestHash, actualHttpAttempts, accountPaused] properties: context: {$ref: '#/components/schemas/EgressContext'} operation: {$ref: '#/components/schemas/EgressOperation'} requestHash: {$ref: '#/components/schemas/EgressRequestHash'} actualHttpAttempts: {type: integer, minimum: 0, maximum: 1} errorCode: {type: string, nullable: true, description: Sanitized local error code; null on success.} upstreamStatus: {type: integer, nullable: true} sourceCode: {type: integer, nullable: true} accountPaused: {type: boolean, description: True on 429 or explicit provider rate limiting; all account request families must pause.} cooldownUntil: {type: string, format: date-time, nullable: true} AttemptPermitResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: code: {type: string, enum: ['200', B0001]} data: oneOf: - {$ref: '#/components/schemas/AttemptPermit'} - {$ref: '#/components/schemas/AuthorizationDenial'} description: Code 200 requires AttemptPermit; code B0001 requires AuthorizationDenial. Lost or malformed responses are uncertain and fail closed with a local pause. CompletionResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: data: {type: boolean, enum: [true], description: Successful acknowledgement confirms durable completion and cooldown persistence; any other result fails closed.} WarehouseQueryRequest: type: object required: [current, size] properties: current: {type: integer, minimum: 1} size: {type: integer, minimum: 1, maximum: 1000} QueryRequest: type: object required: [startTime, endTime, current, size] properties: startTime: {type: string, example: '2026-09-12T08:00:00', description: Source local datetime. Must precede endTime and be no earlier than endTime minus one calendar month. No source timezone inference.} endTime: {type: string, example: '2026-10-12T08:00:00'} current: {type: integer, minimum: 1} size: {type: integer, minimum: 1, maximum: 100, description: 'Local request cap, not supplier quota.'} LocateRequest: type: object required: [deliveryNo, current, size] properties: deliveryNo: {type: string, minLength: 1, maxLength: 128} current: {type: integer, minimum: 1} size: {type: integer, minimum: 1, maximum: 1000} DetailRequest: type: object required: [warehouseCode, customerCode, deliveryNo] properties: warehouseCode: {type: string, minLength: 1, maxLength: 64} customerCode: {type: string, minLength: 1, maxLength: 64} deliveryNo: {type: string, minLength: 1, maxLength: 128} InventoryQueryRequest: type: object required: [warehouseCode, current, size] properties: warehouseCode: {type: string, minLength: 1, maxLength: 64} current: {type: integer, minimum: 1} size: {type: integer, minimum: 1, maximum: 1000} Pagination: type: object required: [total, current, size, pages, sourceCurrent, sourceSize, sourceCode, observedAt, egress] properties: egress: {$ref: '#/components/schemas/EgressMetadata'} total: {type: string, pattern: '^[0-9]+$', description: Required provider total. Long follows platform string serialization.} current: {type: integer} size: {type: integer} pages: {type: integer, minimum: 0} sourceCurrent: {type: integer} sourceSize: {type: integer} sourceCode: {type: integer, enum: [200]} observedAt: {type: string, format: date-time} OrderSummary: type: object required: [deliveryNo, customerCode, whCode, status] properties: deliveryNo: {type: string} sourceNo: {type: string, nullable: true} customerCode: {type: string} whCode: {type: string} status: {type: integer} createTime: {type: string, nullable: true} outboundTime: {type: string, nullable: true} productList: type: array nullable: true description: Per-SKU list quantities; missing items, SKU or qty require one detail supplement for this order. Zero is a known quantity. items: type: object properties: productSku: {type: string, nullable: true} productName: {type: string, nullable: true} qty: {type: number, nullable: true} Warehouse: type: object required: [whCode, warehouseType, status] properties: whCode: {type: string} whNameCn: {type: string, nullable: true} warehouseType: {type: integer} status: {type: integer} timeZone: {type: string, nullable: true, description: Warehouse metadata only.} SkuLine: type: object required: [productSku] properties: productSku: {type: string} productName: {type: string, nullable: true} actualDeliveryQty: {type: number, nullable: true, description: 'BigDecimal actual quantity, unknown is null.'} deliveryQty: {type: number, nullable: true, description: Forecast only; never a replacement for actual quantity.} OrderDetail: type: object required: [deliveryNo, customerCode, whCode, status, skuVOList, sourceCode, observedAt, egress] properties: egress: {$ref: '#/components/schemas/EgressMetadata'} deliveryNo: {type: string} sourceNo: {type: string, nullable: true} customerCode: {type: string} whCode: {type: string} status: {type: integer} orderCreateTime: {type: string, nullable: true} outboundTime: {type: string, nullable: true} skuVOList: {type: array, minItems: 1, items: {$ref: '#/components/schemas/SkuLine'}} sourceCode: {type: integer, enum: [200]} observedAt: {type: string, format: date-time} ProductStock: type: object required: [customerCode, whCode, productSku, productQuality] properties: customerCode: {type: string} whCode: {type: string} productSku: {type: string} productName: {type: string, nullable: true} productQuality: {type: integer} stkQty: {type: number, nullable: true} freezeQty: {type: number, nullable: true} useStkQty: {type: number, nullable: true} transitQty: {type: number, nullable: true} innerBatchNo: {type: string, nullable: true} outBatchNo: {type: string, nullable: true} uniqueKey4tenant: {type: string, nullable: true} OrderPage: allOf: - {$ref: '#/components/schemas/Pagination'} - type: object required: [records] properties: records: {type: array, items: {$ref: '#/components/schemas/OrderSummary'}} WarehousePage: allOf: - {$ref: '#/components/schemas/Pagination'} - type: object required: [records] properties: records: {type: array, items: {$ref: '#/components/schemas/Warehouse'}} InventoryPage: allOf: - {$ref: '#/components/schemas/Pagination'} - type: object required: [records] properties: records: {type: array, items: {$ref: '#/components/schemas/ProductStock'}} ProviderFailure: type: object required: [errorCode, retryable, egress] properties: egress: {$ref: '#/components/schemas/EgressMetadata'} denial: nullable: true allOf: - {$ref: '#/components/schemas/AuthorizationDenial'} description: Well-formed deterministic authority denial, alongside egress; null for other failures. errorCode: {type: string} retryable: {type: boolean, description: Caller scheduling hint only; never authorizes implicit retries or reuse of an attempt. False for 429 and validation session failure.} upstreamStatus: {type: integer, nullable: true} sourceCode: {type: integer, nullable: true} Envelope: type: object required: [code, message, data, timestamp] properties: code: {type: string, description: '200 success; A0401 identity; A0422 validation; B0001 or S0503 provider/configuration failure.'} message: {type: string, description: 'Sanitized fixed local message, never raw supplier content.'} requestId: {type: string, nullable: true} traceId: {type: string, nullable: true} timestamp: {type: string, format: date-time} OrderResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: data: nullable: true oneOf: - {$ref: '#/components/schemas/OrderPage'} - {$ref: '#/components/schemas/ProviderFailure'} WarehouseResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: data: nullable: true oneOf: - {$ref: '#/components/schemas/WarehousePage'} - {$ref: '#/components/schemas/ProviderFailure'} DetailResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: data: nullable: true oneOf: - {$ref: '#/components/schemas/OrderDetail'} - {$ref: '#/components/schemas/ProviderFailure'} InventoryResponse: allOf: - {$ref: '#/components/schemas/Envelope'} - type: object properties: data: nullable: true oneOf: - {$ref: '#/components/schemas/InventoryPage'} - {$ref: '#/components/schemas/ProviderFailure'}