# Shipment API Testing This guide provides specific testing approaches and examples for the Equinix Shipments API, building on the general [API Testing](https://docs.equinix.com/equinix-api/testing.md) principles. ## Shipments API Overview The Shipments API allows you to programmatically schedule and manage inbound and outbound shipments to Equinix IBX data centers, including: * Scheduling inbound shipments (receiving packages) * Scheduling outbound shipments (sending packages) * Updating shipment details * Managing shipment tracking information * Handling carrier information and tracking numbers **Important Note**: All inbound and outbound shipments must be scheduled 24 hours in advance. The Equinix API is a production system that creates real shipment requests. Always follow these testing best practices to ensure you don't create unintended operational work. ## Shipment Testing Workflow Before testing the Shipments API, ensure you have: * Valid API credentials with shipments ordering permissions * Access to Equinix IBX locations where you have cages * Valid cage IDs for your shipment locations * Understanding of carrier requirements and tracking number formats **Creating an Inbound Shipment Request** ``` import requests import json from datetime import datetime, timedelta # Setup authentication auth_url = "https://api.equinix.com/oauth2/v1/token" auth_payload = { "grant_type": "client_credentials", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET" } auth_response = requests.post(auth_url, data=auth_payload) token = auth_response.json()["access_token"] # Test creating an inbound shipment request base_url = "https://api.equinix.com/colocations/v2" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } # Calculate delivery date (24+ hours in advance as required) delivery_date = (datetime.utcnow() + timedelta(days=2)).isoformat() + "Z" # Example: Create an inbound shipment request inbound_payload = { "type": "INBOUND", "requestedDateTime": delivery_date, "cageId": "SV1:01:001MC3", "customerReferenceId": "SHIP-REF-12345", "description": "Server hardware delivery for data center expansion project", "details": { "carrier": "DHL", "carrierTrackingNumbers": [ "1234567890123", "1234567890124" ], "numberOfBoxes": 2, "accountNumber": "12345" }, "purchaseOrder": { "type": "EXISTING", "number": "PO-789456" }, "attachments": [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "name": "packing_list.pdf" } ], "contacts": [ { "type": "TECHNICAL", "registeredUsers": ["tech.admin@example.com"], "availability": "WORK_HOURS", "timezone": "America/New_York" }, { "type": "NOTIFICATION", "registeredUsers": ["notifications@example.com"] } ] } create_response = requests.post( f"{base_url}/orders/shipments", headers=headers, data=json.dumps(inbound_payload) ) # Verify response assert create_response.status_code == 201, f"Failed to create shipment: {create_response.text}" # Extract order ID from Location header order_number = create_response.json()["OrderNumber"] print(f"Successfully created inbound shipment with order ID: {order_number}") ``` **Creating an Outbound Shipment Request** ``` # Example: Create an outbound shipment request outbound_payload = { "type": "OUTBOUND", "requestedDateTime": delivery_date, "cageId": "SV1:01:001MC3", "customerReferenceId": "OUT-SHIP-67890", "description": "Returning defective network equipment to vendor", "details": { "carrier": "FEDEX", "carrierTrackingNumbers": [ "987654321098" ], "numberOfBoxes": 1, "pickupLocation": "Main cage entrance", "specialInstructions": "Handle with care - contains sensitive electronic equipment" }, "purchaseOrder": { "type": "EXEMPTED" }, "contacts": [ { "type": "TECHNICAL", "registeredUsers": ["shipping@example.com"], "availability": "ANYTIME" } ] } outbound_response = requests.post( f"{base_url}/orders/shipments", headers=headers, data=json.dumps(outbound_payload) ) assert outbound_response.status_code == 201, f"Failed to create outbound shipment: {outbound_response.text}" order_number = outbound_response.json()["OrderNumber"] print("Successfully created outbound shipment request with order ID: {order_number}") ``` ## Testing Error Scenarios Always test error handling for your API implementation: ``` # Test with invalid carrier invalid_carrier_payload = { "type": "INBOUND", "requestedDateTime": delivery_date, "cageId": "SV1:01:001MC3", "details": { "carrier": "INVALID_CARRIER", # Invalid carrier "carrierTrackingNumbers": ["1234567890"], "numberOfBoxes": 1 }, "contacts": [ { "type": "TECHNICAL", "registeredUsers": ["test@example.com"] } ] } error_response = requests.post( f"{base_url}/orders/shipments", headers=headers, data=json.dumps(invalid_carrier_payload) ) # Verify error response assert error_response.status_code == 400, f"Expected error but got: {error_response.status_code}" error_data = error_response.json() assert any("carrier" in error["errorMessage"].lower() for error in error_data), "Expected carrier validation error" # Test with insufficient advance notice (less than 24 hours) insufficient_time_payload = { "type": "INBOUND", "requestedDateTime": (datetime.utcnow() + timedelta(hours=12)).isoformat() + "Z", # Only 12 hours advance "cageId": "SV1:01:001MC3", "details": { "carrier": "DHL", "carrierTrackingNumbers": ["1234567890"], "numberOfBoxes": 1 }, "contacts": [ { "type": "TECHNICAL", "registeredUsers": ["test@example.com"] } ] } time_error_response = requests.post( f"{base_url}/orders/shipments", headers=headers, data=json.dumps(insufficient_time_payload) ) assert time_error_response.status_code == 400, f"Expected time validation error but got: {time_error_response.status_code}" ``` ## Best Practices for Shipments API Testing 1. **24-Hour Advance Rule**: Always schedule shipments at least 24 hours in advance 2. **Valid Tracking Numbers**: Use realistic tracking number formats for each carrier 3. **Proper Contact Information**: Ensure contacts are available during delivery windows 4. **Cage Access**: Verify cage IDs and access permissions before testing 5. **International Shipments**: Consider customs requirements for international deliveries 6. **Special Instructions**: Test with various special handling requirements 7. **File Attachments**: Test attachment uploads for packing lists and special instructions 8. **Time Zones**: Handle time zone conversions properly for scheduling 9. **Purchase Orders**: Test different PO types (NEW, EXISTING, EXEMPTED) ## Automation Considerations When building automation around the Shipments API: * Implement validation for the 24-hour advance scheduling requirement * Build in carrier-specific validation for tracking number formats * Include proper error handling for carrier availability and capacity issues * Implement webhook receivers for shipment status updates * Build audit logging for all shipment transactions * Validate cage access and permissions before creating requests * Ensure proper handling of time zones for international locations ## Additional Resources * [Shipments API Reference](https://docs.equinix.com/api-catalog/shipmentsv2.md) * [Smart Hands Service Guide](https://docs.equinix.com/smart-hands.md) * [API Authentication](https://docs.equinix.com/equinix-api/api-authentication.md)