Experimental API
The Experimental API is a Beta-only GraphQL endpoint for customers who are testing new ShipHero API capabilities before they are added to the standard Public API.
Access is not enabled by default. If you want to use the Experimental API, request access through your Customer Success Manager (CSM). Your CSM will confirm whether your account is eligible and enable access for your account.
Once access is enabled, use the standard GraphQL endpoint:
https://public-api.shiphero.com/graphql/experimentalBecause the Experimental API can change frequently, fetch or inspect the latest GraphQL schema from this endpoint before building or updating an integration.
If you have access, please share feedback quickly with your ShipHero contact. Feedback from Beta testers helps shape the final API before it becomes generally available.
Currently Available
Today, the Experimental API can be used to:
- Query available carriers and shipping methods with
carriersandshipping_methodsqueries - Request label printing with the
label_printmutation - Create an inbound shipment from an Advance Shipment Notice with the
inbound_shipment_create_from_asnmutation
Connected Carriers Query
Use the carriers query to retrieve the carriers and shipping methods currently available to your account.
query {
carriers {
request_id
complexity
data {
carrier
carrier_name
shipping_methods {
carrier
carrier_name
method
method_name
}
}
}
}Example response:
{
"data": {
"carriers": {
"request_id": "...",
"complexity": 2,
"data": [
{
"carrier": "amazon",
"carrier_name": "Amazon",
"shipping_methods": [
{
"carrier": "amazon",
"carrier_name": "Amazon",
"method": "UPS_PTP_2ND_DAY_AIR",
"method_name": "UPS 2nd Day Air"
},
{
"carrier": "amazon",
"carrier_name": "Amazon",
"method": "UPS_PTP_GND",
"method_name": "UPS Ground"
}
]
},
{
"carrier": "genericlabel",
"carrier_name": "Generic",
"shipping_methods": [
{
"carrier": "genericlabel",
"carrier_name": "Generic",
"method": "genericlabel",
"method_name": "genericlabel"
}
]
},
{
"carrier": "ups",
"carrier_name": "UPS",
"shipping_methods": [
{
"carrier": "ups",
"carrier_name": "UPS",
"method": "UPS Ground",
"method_name": "UPS Ground"
},
{
"carrier": "ups",
"carrier_name": "UPS",
"method": "UPS Next Day Air",
"method_name": "UPS Next Day Air"
}
]
}
]
}
},
"extensions": {
"throttling": {
"estimated_complexity": 2,
"cost": 1,
"cost_detail": {
"carriers": {
"items_count": 0,
"cost": 1,
"total_cost": 1,
"fields": {}
}
},
"user_quota": {
"credits_remaining": 4003,
"max_available": 4004,
"increment_rate": 60
}
}
}
}Note
The carriers and methods returned by this query can vary by account configuration and by the current Experimental API release.
Label Print Mutation
Use label_print to request label printing for an order. The payload identifies the order, carrier, method, whether an invoice should print, and the package contents.
The mutation returns labels directly under data.label_print.labels. It does not return a top-level shipment object, but each label can include shipment data such as shipment_id and shipment_line_items when those fields are selected.
Required OAuth Scope
The label_print mutation requires the granted OAuth scope:
change:shipmentsIf the issued token does not include this scope, the mutation returns a permission error:
{
"errors": [
{
"message": "Missing required scope(s): change:shipments",
"operation": "label_print",
"field": "label_print",
"request_id": "...",
"code": 7
}
],
"data": {
"label_print": null
}
}Note
Requesting change:shipments in the OAuth flow is not enough. The issued token must actually include the granted change:shipments scope. Some OAuth clients or accounts may not be allowed to receive write scopes.
Mutation
mutation Label_print($data: PrintLabelInput!) {
label_print(data: $data) {
request_id
complexity
labels {
id
legacy_id
account_id
shipment_id
order_id
box_id
box_name
status
tracking_number
alternate_tracking_id
order_number
order_account_id
carrier
shipping_name
shipping_method
cost
box_code
device_id
delivered
picked_up
refunded
needs_refund
profile
partner_fulfillment_id
full_size_to_print
packing_slip
warehouse
warehouse_id
insurance_amount
carrier_account_id
source
created_date
tracking_url
package_number
parcelview_url
tracking_status
in_shipping_container
shipping_container_id
label {
pdf_location
paper_pdf_location
thermal_pdf_location
image_location
}
shipment_line_items {
edges {
cursor
node {
id
legacy_id
line_item_id
shipment_id
shipping_label_id
quantity
line_item {
id
legacy_id
sku
partner_line_item_id
product_id
quantity
price
product_name
option_title
fulfillment_status
quantity_pending_fulfillment
quantity_shipped
warehouse
quantity_allocated
backorder_quantity
custom_options
custom_barcode
eligible_for_return
customs_value
warehouse_id
locked_to_warehouse_id
subtotal
barcode
created_at
updated_at
order_id
promotion_discount
}
}
}
}
}
}
}Variables
This example uses genericlabel and explicit package dimensions.
{
"data": {
"order_id": "{{order_id}}",
"shipping_carrier": "genericlabel",
"shipping_method": "genericlabel",
"print_invoice": false,
"packages": [
{
"dimensions": {
"weight": 0.0625,
"height": 1,
"width": 1,
"length": 1
},
"line_items": [
{
"line_item_id": "{{line_item_id}}",
"quantity": 1
}
]
}
]
}
}Before calling label_print, use the carriers query to retrieve the carrier and method values available for the account. For genericlabel, the carrier result looks like this:
{
"carrier": "genericlabel",
"carrier_name": "Generic",
"shipping_methods": [
{
"carrier": "genericlabel",
"carrier_name": "Generic",
"method": "genericlabel",
"method_name": "genericlabel"
}
]
}Package Dimensions Input
PrintLabelPackageInput supports dimensions as an alternative to carrier_box_code or custom_box_id. You can also send custom_box_id instead of carrier_box_code when you want to print the label with a custom box.
The PrintLabelDimensionsInput fields are:
weight: Float!
height: Float!
width: Float!
length: Float!Example:
{
"dimensions": {
"weight": 0.0625,
"height": 1,
"width": 1,
"length": 1
}
}Response
Example response:
{
"data": {
"label_print": {
"request_id": "...",
"complexity": 15,
"labels": [
{
"id": "...",
"legacy_id": 12345,
"shipment_id": "...",
"order_id": "{{order_id}}",
"status": "valid",
"tracking_number": "",
"order_number": "EXAMPLE-1001",
"carrier": "genericlabel",
"shipping_method": "genericlabel",
"cost": "0.00",
"warehouse": "Primary",
"warehouse_id": "12345",
"created_date": "2026-06-22T19:58:51",
"tracking_url": "",
"package_number": 1,
"tracking_status": "UNKNOWN",
"label": {
"pdf_location": "https://example.com/shipping-labels/example-1001/label.pdf",
"paper_pdf_location": "https://example.com/shipping-labels/example-1001/paper-label.pdf",
"thermal_pdf_location": "https://example.com/shipping-labels/example-1001/thermal-label.pdf",
"image_location": "[\"https://example.com/shipping-labels/example-1001/label.pdf\", \"https://example.com/shipping-labels/example-1001/label.png\"]"
},
"shipment_line_items": {
"edges": [
{
"cursor": "...",
"node": {
"id": "...",
"legacy_id": 12345,
"line_item_id": "{{line_item_id}}",
"shipment_id": "...",
"shipping_label_id": "...",
"quantity": 1,
"line_item": {
"id": "{{line_item_id}}",
"legacy_id": 12345,
"sku": "EXAMPLE-SKU",
"product_name": "Example Product",
"quantity": 1,
"quantity_shipped": 1,
"order_id": "{{order_id}}"
}
}
}
]
}
}
]
}
}
}Note
For generic labels, tracking_number and tracking_url may be empty strings.
Note
The image_location field is returned as a JSON-encoded string containing label asset URLs.
Create Inbound Shipment from ASN
Use inbound_shipment_create_from_asn to create an inbound shipment from an Advance Shipment Notice (ASN): the inbound counterpart to a shipping label. Where a supplier would otherwise send an EDI 856 or EDI 943 document, this mutation accepts a normalized JSON payload describing what is arriving, and creates the corresponding ShipHero records so the shipment can be received at the dock.
From a single submission the mutation:
- Creates a Purchase Order and its line items (one per SKU).
- Creates an Inbound Shipment linked to that Purchase Order, with status
PENDING_ARRIVAL. - Creates the expected inbound line items (quantity per SKU).
- Stores the full ASN payload so warehouse workers can look it up by scanning any container barcode (SSCC) when the truck arrives.
The mutation always returns a 997-style acknowledgment envelope describing whether the ASN was Accepted or Rejected, echoing the original document metadata back to the sender.
Required OAuth Scope
The inbound_shipment_create_from_asn mutation requires the granted OAuth scope:
change:inbound_shipmentsIf the issued token does not include this scope, the mutation returns a permission error:
{
"errors": [
{
"message": "Missing required scope(s): change:inbound_shipments",
"operation": "inbound_shipment_create_from_asn",
"field": "inbound_shipment_create_from_asn",
"request_id": "...",
"code": 7
}
],
"data": {
"inbound_shipment_create_from_asn": null
}
}Note
Requesting change:inbound_shipments in the OAuth flow is not enough. The issued token must actually include the granted scope. Some OAuth clients or accounts may not be allowed to receive write scopes.
Packing Structure
The payload describes packaging with a recursive packing_units tree, which lets a single input shape represent both flat (943-style) and hierarchical (856-style) shipments:
- A packing unit with nested
packing_unitsis treated as a pallet. - A packing unit without nested
packing_unitsis treated as a box, and carries the line items directly.
Hierarchical (856-style): Flat (943-style):
packing_units: packing_units:
- sscc: PALLET-AAA - sscc: BOX-BBB
packing_units: line_items:
- sscc: BOX-BBB - sku: WIDGET-A, qty: 50
line_items:
- sku: WIDGET-A, qty: 50Each unit optionally carries an sscc (the shipper’s GS1 container barcode) and a po_number. Purchase Orders are grouped by the distinct po_number values found across the tree.
Mutation
mutation InboundShipmentCreateFromAsn($data: CreateInboundShipmentFromAsnInput!) {
inbound_shipment_create_from_asn(data: $data) {
request_id
complexity
document_type
document_id
created_at
interchange {
sender_id
sender_qualifier
receiver_id
receiver_qualifier
control_number
}
functional_group {
functional_id_code
version
control_number
original_transaction_set_id
}
acknowledgment {
original_transaction_set_id
original_external_shipment_id
original_vendor_name
original_ship_date
accepted_count
rejected_count
status
status_description
}
result {
inbound_shipment {
id
legacy_id
bol_reference
warehouse_id
status
carrier
pro_number
container_number
scheduled_at
estimated_truck_arrival
pallets_quantity
number_carton
booking_contact_name
booking_contact_email
booking_contact_number
}
purchase_orders {
id
legacy_id
po_number
line_items {
edges {
node {
id
sku
quantity
}
}
}
}
asn_document_id
}
errors {
code
segment
element
qualifier
value
message
}
}
}Note
result.inbound_shipment and result.purchase_orders are the standard InboundShipment and PurchaseOrder types, so you can select any field those types expose (including the full line_items and purchase_orders connections). Object ids are the encoded global id; use legacy_id when you need the numeric id for legacy endpoints. The booking_contact_* fields require the view:pii scope.
Variables
This example is a single-box (943-style) shipment for one SKU.
{
"data": {
"external_shipment_id": "BOL-001",
"warehouse_id": "{{warehouse_id}}",
"vendor_name": "Acme Co",
"edi_type": "943",
"packing_units": [
{
"sscc": "00012345600000000018",
"po_number": "PO-001",
"line_items": [
{
"product_ids": [{ "qualifier": "SK", "value": "{{sku}}" }],
"quantity": 10,
"uom": "EA"
}
]
}
]
}
}Required Input Fields
| Field | Description |
|---|---|
external_shipment_id | BOL, shipment ID, or any shipper reference. Must be unique per account. |
warehouse_id | The receiving warehouse (encoded Public API id). |
vendor_name | The shipping vendor / trading partner name. |
edi_type | The source document type: "856" or "943". |
packing_units | Recursive list of pallets and boxes carrying the line items. |
Each line item requires product_ids, quantity, and uom. Supported uom codes are EA, CA, PL, CT, and BX.
Optional shipment fields include edi_version, trading_partner_id, carrier, pro_number, container_number, ship_date, estimated_arrival, the vendor_* address fields, and the booking_contact_* fields.
Note
In the current release, products can only be identified by ShipHero SKU. Send qualifier: "SK" and set value to the exact SKU. Other identifiers (UPC, GTIN, vendor part number) are not yet resolved.
Response
When the ASN is accepted, acknowledgment.status is "A" and result contains the created records.
{
"data": {
"inbound_shipment_create_from_asn": {
"request_id": "...",
"complexity": 15,
"document_type": "997",
"document_id": "997-20260715-1a2b3c4d",
"created_at": "2026-07-15T12:00:00",
"interchange": {
"sender_id": "SHIPHERO",
"sender_qualifier": "ZZ",
"receiver_id": "ACME-EDI",
"receiver_qualifier": "ZZ",
"control_number": "a1b2c3d4e"
},
"functional_group": {
"functional_id_code": "FA",
"version": "004010",
"control_number": "f5e6d7c8b",
"original_transaction_set_id": "943"
},
"acknowledgment": {
"original_transaction_set_id": "943",
"original_external_shipment_id": "BOL-001",
"original_vendor_name": "Acme Co",
"original_ship_date": null,
"accepted_count": 1,
"rejected_count": 0,
"status": "A",
"status_description": "Accepted"
},
"result": {
"inbound_shipment": {
"id": "SW5ib3VuZFNoaXBtZW50Ojc4OQ==",
"legacy_id": 789,
"bol_reference": "BOL-001",
"warehouse_id": "V2FyZWhvdXNlOjQ1Ng==",
"status": "PENDING_ARRIVAL",
"carrier": null,
"pro_number": null,
"container_number": null,
"scheduled_at": null,
"estimated_truck_arrival": null,
"pallets_quantity": 0,
"number_carton": 1,
"booking_contact_name": null,
"booking_contact_email": null,
"booking_contact_number": null
},
"purchase_orders": [
{
"id": "UHVyY2hhc2VPcmRlcjoxMDE=",
"legacy_id": 101,
"po_number": "PO-001",
"line_items": {
"edges": [
{ "node": { "id": "...", "sku": "WIDGET-A", "quantity": 10 } }
]
}
}
],
"asn_document_id": "..."
},
"errors": []
}
}
}Rejection Envelope
Validation failures do not raise a GraphQL error. Instead the mutation returns a rejection envelope: acknowledgment.status is "R", result is null, and errors lists what went wrong. Each error maps back to the originating EDI segment where applicable.
{
"data": {
"inbound_shipment_create_from_asn": {
"document_type": "997",
"acknowledgment": {
"status": "R",
"status_description": "Rejected",
"accepted_count": 0,
"rejected_count": 1
},
"result": null,
"errors": [
{
"code": "PRODUCT_NOT_FOUND",
"segment": "W04",
"element": "W0406",
"qualifier": "SK",
"value": "SKU-DOES-NOT-EXIST",
"message": "Could not resolve product identifiers: SKU:SKU-DOES-NOT-EXIST"
}
]
}
}
}The error code values are:
| Code | Meaning |
|---|---|
WAREHOUSE_NOT_FOUND | warehouse_id does not belong to the account. |
DUPLICATE_SHIPMENT | An inbound shipment already exists for this external_shipment_id. |
NO_LINE_ITEMS | The shipment contains no line items. |
PRODUCT_NOT_FOUND | A SKU could not be resolved for the account. |
INVALID_SSCC_LENGTH | An SSCC exceeds the 22-character limit. |
AMBIGUOUS_LOT_NOT_SUPPORTED | An item references multiple lots without a per-lot quantity. |
Note
external_shipment_id must be unique per account. Re-submitting the same value returns a DUPLICATE_SHIPMENT rejection rather than overwriting the existing shipment.