POST /api/exports/planPOST /api/exports/executeGET /api/exports/{id}
Use them in that order.
plancomputes what should happenexecuteperforms side effectsinspectis the canonical read model afterward
Machine-readable contract:
Related docs:
/docs/concepts/protocol/docs/concepts/export-lifecycle/docs/concepts/artifacts/docs/concepts/broker-submission/docs/guides/first-export/docs/guides/warehouse-integration/docs/guides/automation-agents
#Authentication
All endpoints on this page require bearer authentication.
Authorization: Bearer <api-key>For write endpoints:
accountIdin 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
{
"accountId": "acc_123",
"warehouseId": "wh_123",
"brokerId": "br_123",
"destinationCountry": "US",
"orderIds": ["ord_123", "ord_124"],
"dryRun": false
}#cURL example
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
{
"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:
{
"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-Keyon planning writes. - Reusing the same key with a different logical payload can return
409 IDEMPOTENCY_CONFLICT.
#Determinism
planHashis the important machine field here.- If two planning runs produce the same
planHash, agents can treat them as the same planning snapshot. - Store
planHashif you compare planning results over time or across retries.
#When not to execute
- If
readinessStateisBLOCKED, fix the underlying validation issues first. - If
readinessStateisREVIEW_REQUIRED, use policy to decide whether to continue. - Do not infer readiness from HTTP
200alone.
#Notes for AI agents and automation clients
- Treat
validationMessages,flags, andmissing_dataas action signals. - Use
planHashto 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
{
"accountId": "acc_123",
"batchId": "batch_123"
}#cURL example
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
{
"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:
{
"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
artifactsgive you metadata immediately after execution.brokerSubmissionis 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_idfor direct customs package lookup. - Do not assume broker submission succeeded just because execution returned
200; readbrokerSubmission.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
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
{
"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:
{
"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, ornotifications. - 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
planHashplusstatusto detect whether a plan was executed, changed, or remained stable. - Use
events,notifications, andbrokerSubmissionas 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 |