API

Exports API

These three endpoints are the core EntryGo export protocol:

  • POST /api/exports/plan
  • POST /api/exports/execute
  • GET /api/exports/{id}

Use them in that order.

  • plan computes what should happen
  • execute performs side effects
  • inspect is the canonical read model afterward

Machine-readable contract:

Related docs:

#Authentication

All endpoints on this page require bearer authentication.

http
Authorization: Bearer <api-key>

For write endpoints:

  • accountId in the body must match the API key scope
  • missing or invalid keys return 401 INVALID_API_KEY
  • mismatched payload account returns 403 INVALID_ACCOUNT_SCOPE

#POST /api/exports/plan

#Purpose

Create a deterministic export plan from account-scoped orders, a warehouse, and a broker.

This endpoint does not execute the export. It evaluates readiness and returns the planning snapshot you should inspect before side effects.

#Request body

Field Type Required Description
accountId string yes EntryGo account ID
warehouseId string yes Warehouse in the same account
brokerId string yes Broker in the same account
orderIds array of strings yes EntryGo order IDs from order sync
destinationCountry string no Two-letter country code, defaults to US
batchId string no Existing batch ID to re-plan
dryRun boolean no If true, compute a plan without durable execution setup
idempotencyKey string no Optional body-level idempotency key

Header:

Header Required Description
Idempotency-Key no Preferred idempotency key for retriable writes

If both header and body idempotency keys are present, the header wins.

#JSON request example

json
{
  "accountId": "acc_123",
  "warehouseId": "wh_123",
  "brokerId": "br_123",
  "destinationCountry": "US",
  "orderIds": ["ord_123", "ord_124"],
  "dryRun": false
}

#cURL example

bash
curl -X POST http://localhost:3000/api/exports/plan \
  -H "authorization: Bearer shpbndl_live_..." \
  -H "content-type: application/json" \
  -H "idempotency-key: plan-acc-123-001" \
  -d '{
    "accountId": "acc_123",
    "warehouseId": "wh_123",
    "brokerId": "br_123",
    "destinationCountry": "US",
    "orderIds": ["ord_123", "ord_124"],
    "dryRun": false
  }'

#Success response

Important fields:

Field Description
exportId Export batch ID
state Normalized export lifecycle state
batch_id Same batch ID in snake_case
status Planning status
planHash Deterministic fingerprint of the planning snapshot
readinessState READY, REVIEW_REQUIRED, or BLOCKED
complianceScore Readiness score from 0 to 100
validationMessages Structured validation results
included_order_ids Orders included in the plan
excluded_order_ids Orders not included
flags Planning flags to surface downstream
missing_data Missing data signals
recommended_strategy Human-readable strategy summary
estimated_import_savings_usd Estimated savings value
next_recommended_action Suggested next step

#JSON response example

json
{
  "exportId": "batch_123",
  "state": "BATCHED",
  "batch_id": "batch_123",
  "planHash": "planhash_abc123",
  "status": "BATCHED",
  "readinessState": "REVIEW_REQUIRED",
  "complianceScore": 74,
  "validationMessages": [
    {
      "code": "METALS_REVIEW_RECOMMENDED",
      "message": "Product JKT-001 should receive metals review before execution.",
      "severity": "warning",
      "field": "orders[0].items[0].product.section_232",
      "product_id": "prod_hoodie_001",
      "order_id": "ord_123"
    }
  ],
  "included_order_ids": ["ord_123", "ord_124"],
  "excluded_order_ids": [],
  "flags": ["human_review_recommended"],
  "missing_data": [],
  "recommended_strategy": "Group U.S.-bound orders by operational readiness and generate an export batch before broker-facing review.",
  "estimated_import_savings_usd": 18.5,
  "next_recommended_action": "review_flags_then_execute",
  "validation": {
    "readiness_state": "REVIEW_REQUIRED",
    "compliance_score": 74,
    "validation_messages": [
      {
        "code": "METALS_REVIEW_RECOMMENDED",
        "message": "Product JKT-001 should receive metals review before execution.",
        "severity": "warning",
        "field": "orders[0].items[0].product.section_232",
        "product_id": "prod_hoodie_001",
        "order_id": "ord_123"
      }
    ]
  }
}

#Error response example

Order reference failure:

json
{
  "errorCode": "INVALID_ORDER_REFERENCE",
  "message": "One or more orders could not be resolved.",
  "details": {
    "requestedOrderIds": ["ord_123", "ord_missing"],
    "foundOrderIds": ["ord_123"]
  }
}

#Behavioral notes

#Idempotency

  • Use Idempotency-Key on planning writes.
  • Reusing the same key with a different logical payload can return 409 IDEMPOTENCY_CONFLICT.

#Determinism

  • planHash is the important machine field here.
  • If two planning runs produce the same planHash, agents can treat them as the same planning snapshot.
  • Store planHash if you compare planning results over time or across retries.

#When not to execute

  • If readinessState is BLOCKED, fix the underlying validation issues first.
  • If readinessState is REVIEW_REQUIRED, use policy to decide whether to continue.
  • Do not infer readiness from HTTP 200 alone.

#Notes for AI agents and automation clients

  • Treat validationMessages, flags, and missing_data as action signals.
  • Use planHash to avoid redundant decision-making when a plan has not materially changed.
  • Planning is not proof that execution happened. Always inspect or execute explicitly afterward.

#POST /api/exports/execute

#Purpose

Execute a previously planned export batch.

This is the side-effecting phase of the protocol. It creates or reuses customs package state, materializes artifact metadata, records notifications, and returns broker submission state.

#Request body

Field Type Required Description
accountId string yes EntryGo account ID
batchId string yes Export batch ID returned by planning
idempotencyKey string no Optional body-level idempotency key

Header:

Header Required Description
Idempotency-Key no Preferred idempotency key for execute

If both header and body keys are present, the header wins.

#JSON request example

json
{
  "accountId": "acc_123",
  "batchId": "batch_123"
}

#cURL example

bash
curl -X POST http://localhost:3000/api/exports/execute \
  -H "authorization: Bearer shpbndl_live_..." \
  -H "content-type: application/json" \
  -H "idempotency-key: execute-batch-123-001" \
  -d '{
    "accountId": "acc_123",
    "batchId": "batch_123"
  }'

#Success response

Important fields:

Field Description
exportId Export batch ID
state Normalized lifecycle state
batch_id Same batch ID in snake_case
status Execution status
customs_package_id Customs package created or reused by execution
planHash Planning fingerprint for the executed batch
artifacts Artifact metadata generated for the package
notification_ids Notification IDs created during execution
notifications Current contract-compatible duplicate ID list
execution_timestamp Execution timestamp
brokerSubmission Broker submission record, or null

#JSON response example

json
{
  "exportId": "batch_123",
  "state": "COMPLETED",
  "batch_id": "batch_123",
  "status": "COMPLETED",
  "customs_package_id": "cp_123",
  "planHash": "planhash_abc123",
  "artifacts": [
    {
      "artifact_type": "cci_human_readable",
      "filename": "commercial_customs_invoice.html",
      "generated_at": "2026-03-11T16:10:00.000Z"
    },
    {
      "artifact_type": "cci_csv",
      "filename": "commercial_customs_invoice.csv",
      "generated_at": "2026-03-11T16:10:00.000Z"
    },
    {
      "artifact_type": "manifest",
      "filename": "manifest.json",
      "generated_at": "2026-03-11T16:10:00.000Z"
    }
  ],
  "notification_ids": ["notif_123", "notif_456"],
  "notifications": ["notif_123", "notif_456"],
  "execution_timestamp": "2026-03-11T16:10:00.000Z",
  "brokerSubmission": {
    "id": "bes_123",
    "submission_type": "EMAIL",
    "status": "sent",
    "recipient_email": "broker@example.com",
    "subject": "EntryGo broker submission cp_ch123456",
    "sent_at": "2026-03-11T16:10:00.000Z",
    "error_message": null
  }
}

#Error response example

State conflict:

json
{
  "errorCode": "INVALID_EXPORT_STATE",
  "message": "Export batch cannot be executed from current state.",
  "details": {
    "currentState": "COMPLETED"
  }
}

#Behavioral notes

#Idempotency

  • Execute should always be called with an idempotency key.
  • If the client loses the response, retry the same request with the same key.
  • If the same key is reused with a different payload, expect 409 IDEMPOTENCY_CONFLICT.

#Retry guidance

  • Safe retry: same batchId, same authenticated account, same idempotency key.
  • Not safe retry: changing the request and reusing the same key.
  • After any ambiguous write outcome, follow with GET /api/exports/{id}.

#Artifact and broker state

  • artifacts give you metadata immediately after execution.
  • brokerSubmission is the broker-facing state exposed by this write.
  • For ongoing truth, use inspect rather than caching execute output forever.

#Notes for AI agents and automation clients

  • Treat execute as side-effecting and inspect immediately after success or uncertainty.
  • Persist customs_package_id for direct customs package lookup.
  • Do not assume broker submission succeeded just because execution returned 200; read brokerSubmission.status.

#GET /api/exports/{id}

#Purpose

Return the current machine-readable state of an export batch.

This is the canonical read endpoint after planning or execution. It combines planning state, execution state, customs package state, artifacts, notifications, events, and snapshot data.

#Authentication

Bearer API key required. Account scoping is enforced through the export lookup.

#Path parameter

Parameter Type Description
id string Export batch ID

#cURL example

bash
curl http://localhost:3000/api/exports/batch_123 \
  -H "authorization: Bearer shpbndl_live_..."

#Success response

Important top-level fields:

Field Description
exportId Export batch ID
status Current normalized status
state Current normalized lifecycle state
readinessState Current readiness view
complianceScore Current readiness score
validationMessages Structured validation results
planHash Planning fingerprint
artifacts Artifact metadata
customsPackage Customs package summary or null
notifications Notification objects
brokerSubmission Broker submission object or null
snapshot Current machine snapshot used for inspection
events Export event stream

#JSON response example

json
{
  "exportId": "batch_123",
  "status": "COMPLETED",
  "state": "COMPLETED",
  "readinessState": "REVIEW_REQUIRED",
  "complianceScore": 74,
  "validationMessages": [
    {
      "code": "METALS_REVIEW_RECOMMENDED",
      "message": "Product JKT-001 should receive metals review before execution.",
      "severity": "warning",
      "field": "orders[0].items[0].product.section_232",
      "product_id": "prod_hoodie_001",
      "order_id": "ord_123"
    }
  ],
  "planHash": "planhash_abc123",
  "artifacts": [
    {
      "artifact_type": "cci_human_readable",
      "filename": "commercial_customs_invoice.html",
      "generated_at": "2026-03-11T16:10:00.000Z"
    },
    {
      "artifact_type": "manifest",
      "filename": "manifest.json",
      "generated_at": "2026-03-11T16:10:00.000Z"
    }
  ],
  "export": {
    "batch_id": "batch_123",
    "status": "COMPLETED",
    "planHash": "planhash_abc123",
    "lifecycle_state": "COMPLETED",
    "plan": {
      "readinessState": "REVIEW_REQUIRED",
      "included_order_ids": ["ord_123", "ord_124"]
    },
    "execution": {
      "customs_package_id": "cp_123",
      "executed_at": "2026-03-11T16:10:00.000Z"
    },
    "inspection": {
      "artifact_count": 2,
      "notification_count": 2
    }
  },
  "batch": {
    "id": "batch_123",
    "state": "COMPLETED",
    "raw_state": "completed",
    "warehouse_id": "wh_123",
    "broker_id": "br_123",
    "destination_country": "US",
    "estimated_savings_usd": 18.5,
    "next_recommended_action": "review_flags_then_execute",
    "created_at": "2026-03-11T16:00:00.000Z",
    "executed_at": "2026-03-11T16:10:00.000Z"
  },
  "customsPackage": {
    "id": "cp_123",
    "packageNumber": "cp_ch123456",
    "state": "sent_to_broker"
  },
  "notifications": [
    {
      "id": "notif_123",
      "channel": "EMAIL",
      "status": "sent",
      "created_at": "2026-03-11T16:10:00.000Z"
    }
  ],
  "brokerSubmission": {
    "id": "bes_123",
    "submission_type": "EMAIL",
    "status": "sent",
    "recipient_email": "broker@example.com",
    "subject": "EntryGo broker submission cp_ch123456",
    "sent_at": "2026-03-11T16:10:00.000Z",
    "error_message": null
  },
  "snapshot": {
    "included_order_ids": ["ord_123", "ord_124"],
    "planHash": "planhash_abc123"
  },
  "events": [
    {
      "id": "evt_123",
      "type": "export.executed",
      "created_at": "2026-03-11T16:10:00.000Z",
      "payload": {
        "batchId": "batch_123"
      }
    }
  ]
}

#Error response example

Not found:

json
{
  "errorCode": "NOT_FOUND",
  "message": "Export batch not found."
}

#Behavioral notes

#Inspect is the source of truth

  • After any write, inspect is the best place to confirm what actually happened.
  • It is the right endpoint for polling.
  • It is the right endpoint for reconciliation after timeouts or partial failures.

#Polling guidance

  • Poll inspect, not execute.
  • Stop polling when the fields you care about have stabilized: for example status, artifacts, brokerSubmission, or notifications.
  • Use backoff rather than tight loops.

#Stable contract

  • Top-level normalized fields are the easiest contract surface for clients.
  • Nested objects provide more detail when needed.

#Notes for AI agents and automation clients

  • Branch on inspect state instead of inferring state from local workflow assumptions.
  • Use planHash plus status to detect whether a plan was executed, changed, or remained stable.
  • Use events, notifications, and brokerSubmission as structured outcome signals.

#Common errors across export endpoints

Status errorCode Typical meaning
400 INVALID_REQUEST Invalid JSON
400 VALIDATION_ERROR Schema validation failed
400 INVALID_ORDER_REFERENCE One or more order IDs were invalid
400 INVALID_ACCOUNT_SCOPE Warehouse or broker is outside current account scope
401 INVALID_API_KEY Missing or invalid API key
403 INVALID_ACCOUNT_SCOPE accountId does not match API key scope
404 NOT_FOUND Export batch not found in account scope
409 IDEMPOTENCY_CONFLICT Same idempotency key reused for a different payload
409 INVALID_EXPORT_STATE Requested transition is invalid for the current export state