API Integration Guide

Purpose

This guide provides developers and IT teams with the information needed to integrate custom systems or proprietary platforms with EFS using our XML API. This is a technical overview and quick-start guide for building custom integrations.

1. Overview of EFS XML API

What is the EFS XML API?

The EFS XML API allows developers to programmatically interact with our fulfillment system to:

  • Submit orders for fulfillment
  • Retrieve order status and tracking information
  • Update inventory levels
  • Manage product information
  • Query shipping rates

When to Use the API

Use the XML API when:

  • ✓ Your platform doesn’t have a pre-built integration
  • ✓ You have a custom-built ecommerce system
  • ✓ You’re integrating internal business software (ERP, OMS, etc.)
  • ✓ You need advanced automation beyond standard integrations
  • ✓ You’re building a SaaS product that needs fulfillment integration
  • ✓ You require custom business logic or workflows

Don’t use the API when:

  • ✗ Your platform has a pre-built integration (use Integration Portal instead)
  • ✗ You can use Bulk CSV Upload (simpler for manual imports)
  • ✗ You have limited development resources
  • ✗ Your order volume is very low (manual entry may be simpler)

API Capabilities

Order Management:

  • Submit new orders
  • Retrieve order status
  • Get tracking information
  • Cancel orders (if not yet shipped)
  • Update order details (limited)

Inventory Management:

  • Query current inventory levels
  • Update inventory quantities
  • Get inventory activity history
  • Set low stock thresholds

Product Management:

  • Add new products
  • Update product information
  • Retrieve product details
  • Manage SKU mappings

Shipping:

  • Get shipping rate quotes
  • Retrieve available shipping methods
  • Query carrier information

Reporting:

  • Order history reports
  • Inventory reports
  • Shipping reports
  • Billing information

2. Prerequisites

Required Information

Before beginning API integration, gather the following:

☐ FCP Account Setup

  • Active eFulfillment Service account
  • Products/SKUs entered in FCP
  • Inventory received at EFS warehouse

☐ API Credentials

  • Merchant Token (authentication key)
  • Merchant ID (account identifier)

☐ Technical Requirements

  • Development environment capable of HTTP POST requests
  • XML parsing/generation capability
  • Secure credential storage
  • HTTPS/SSL support

☐ Development Resources

  • Developer with XML/API experience
  • Access to testing environment
  • Ability to handle error responses
  • Logging/monitoring capability

Getting API Credentials

Step 1: Access Your Credentials

  1. Log into your Fulfillment Control Panel (FCP)
  2. Go to Client Info → My Settings
  3. Scroll to Integration Credentials section
  4. You’ll see:
    • Merchant ID: Your unique account identifier (numeric)
    • Merchant Token: Your API authentication key (alphanumeric string)

Step 2: Secure Your Credentials

⚠️ Important Security Notes:

  • Merchant Token provides full access to your account
  • Never expose token in client-side code or public repositories
  • Store securely using environment variables or secrets management
  • Rotate token if compromised (contact support)
  • Use HTTPS only for all API calls

Step 3: Test Credentials

Before full integration, verify credentials work:

  • Use a simple API call (e.g., inventory query)
  • Confirm authentication succeeds
  • Verify you receive valid XML response

3. API Basics

Endpoint URL

All API requests are sent to:

https://www.efulfillmentservice.com/xml_api_page.php

Method: POST Content-Type: application/x-www-form-urlencoded Protocol: HTTPS only (HTTP not supported)

Authentication

Every API request must include authentication credentials in the XML payload:

xml

YOUR_MERCHANT_ID
YOUR_MERCHANT_TOKEN
```

These tags appear at the beginning of all request XML documents.

### Request Format

**General structure:**
```
POST https://www.efulfillmentservice.com/xml_api_page.php
Content-Type: application/x-www-form-urlencoded

xml_page=[XML_CONTENT_HERE]

Key points:

  • Parameter name is xml_page
  • XML content must be URL-encoded
  • XML must be properly formatted and valid
  • Include XML declaration: <?xml version=”1.0″ encoding=”UTF-8″?>

Response Format

All responses are XML documents.

Success response structure:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Message>Request processed successfully</Message>
    <!-- Additional response data -->
</Status>

Error response structure:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Error</State>
    <Message>Error description here</Message>
    <ErrorCode>ERROR_CODE</ErrorCode>
</Status>

Common Response States

State Meaning Action
Success Request completed successfully Process response data
Error Request failed Review error message and correct
Warning Request completed with warnings Review warnings, may need adjustment
Pending Request queued for processing Poll for status update

4. Core API Operations

Operation 1: Submit Order

Submit a new order for fulfillment.

Request Type: PostOrders

Basic Example:

<?xml version="1.0" encoding="UTF-8"?>
<PostOrders>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken><Order>
    <OrderNumber>ORD-2025-001</OrderNumber>
    <OrderDate>2025-11-14</OrderDate>    <ShipTo>
        <Name>John Smith</Name>
        <Address1>123 Main Street</Address1>
        <Address2>Apt 4B</Address2>
        <City>San Francisco</City>
        <State>CA</State>
        <Zip>94102</Zip>
        <Country>US</Country>
        <Phone>415-555-1234</Phone>
        <Email>[email protected]</Email>
    </ShipTo>    <ShipMethod>GROUND</ShipMethod>    <Items>
        <Item>
            <SKU>PRODUCT-001</SKU>
            <Quantity>2</Quantity>
        </Item>
        <Item>
            <SKU>PRODUCT-002</SKU>
            <Quantity>1</Quantity>
        </Item>
    </Items>    <SpecialInstructions>Handle with care</SpecialInstructions>
</Order>
</PostOrders>

Required Fields:

  • MerchantId
  • MerchantToken
  • OrderNumber (must be unique)
  • ShipTo (Name, Address1, City, State, Zip, Country)
  • ShipMethod (valid EFS shipping code)
  • Items (at least one Item with SKU and Quantity)

Optional Fields:

  • OrderDate (defaults to current date)
  • Address2
  • Phone
  • Email
  • SpecialInstructions
  • Gift message
  • Signature required flag
  • Insurance value

Response:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Message>Order accepted</Message>
    <OrderNumber>ORD-2025-001</OrderNumber>
    <EFSOrderNumber>EFS123456</EFSOrderNumber>
</Status>

Common Errors:

  • “Duplicate order number” – OrderNumber already exists
  • “Invalid SKU” – SKU not found in FCP
  • “Invalid ship method” – ShipMethod code not recognized
  • “Invalid address” – Address validation failed

Operation 2: Get Order Status

Query the status of one or more orders.

Request Type: GetOrderStatus

Example:

<?xml version="1.0" encoding="UTF-8"?>
<GetOrderStatus>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <OrderNumber>ORD-2025-001</OrderNumber>
</GetOrderStatus>

Or query multiple orders:

<?xml version="1.0" encoding="UTF-8"?>
<GetOrderStatus>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <StartDate>2025-11-01</StartDate>
    <EndDate>2025-11-14</EndDate>
</GetOrderStatus>

Response:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Orders>
        <Order>
            <OrderNumber>ORD-2025-001</OrderNumber>
            <EFSOrderNumber>EFS123456</EFSOrderNumber>
            <Status>Shipped</Status>
            <TrackingNumber>1Z999AA10123456784</TrackingNumber>
            <Carrier>UPS</Carrier>
            <ShipDate>2025-11-13</ShipDate>
            <Items>
                <Item>
                    <SKU>PRODUCT-001</SKU>
                    <QuantityOrdered>2</QuantityOrdered>
                    <QuantityShipped>2</QuantityShipped>
                </Item>
            </Items>
        </Order>
    </Orders>
</Status>

Order Status Values:

  • Pending – Order received, not yet processed
  • Processing – Order being picked/packed
  • Shipped – Order shipped (includes tracking)
  • Backorder – Waiting for inventory
  • Canceled – Order canceled
  • On Hold – Order held for review

Operation 3: Get Inventory Levels

Query current inventory quantities.

Request Type: GetInventory

Example – All Products:

<?xml version="1.0" encoding="UTF-8"?>
<GetInventory>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
</GetInventory>

Example – Specific SKU:

<?xml version="1.0" encoding="UTF-8"?>
<GetInventory>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <SKU>PRODUCT-001</SKU>
</GetInventory>

Response:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Inventory>
        <Item>
            <SKU>PRODUCT-001</SKU>
            <Description>Widget - Blue</Description>
            <QuantityAvailable>150</QuantityAvailable>
            <QuantityHurt>5</QuantityHurt>
            <QuantityAllocated>12</QuantityAllocated>
            <QuantityBackorder>0</QuantityBackorder>
            <BinLocation>A-12-3</BinLocation>
        </Item>
        <Item>
            <SKU>PRODUCT-002</SKU>
            <Description>Widget - Red</Description>
            <QuantityAvailable>87</QuantityAvailable>
            <QuantityHurt>2</QuantityHurt>
            <QuantityAllocated>8</QuantityAllocated>
            <QuantityBackorder>0</QuantityBackorder>
            <BinLocation>A-12-4</BinLocation>
        </Item>
    </Inventory>
</Status>

Quantity Types:

  • QuantityAvailable – Sellable inventory (“Good” inventory)
  • QuantityHurt – Damaged/unsellable inventory
  • QuantityAllocated – Committed to pending orders
  • QuantityBackorder – On backorder status

Operation 4: Update Inventory

Adjust inventory quantities programmatically.

Request Type: UpdateInventory

Example:

<?xml version="1.0" encoding="UTF-8"?>
<UpdateInventory>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <Items>
        <Item>
            <SKU>PRODUCT-001</SKU>
            <Quantity>150</Quantity>
            <AdjustmentType>Set</AdjustmentType>
            <Reason>Inventory correction</Reason>
        </Item>
    </Items>
</UpdateInventory>

Adjustment Types:

  • Set – Set inventory to specific quantity
  • Add – Add to current quantity (e.g., Add 50)
  • Subtract – Subtract from current quantity (e.g., Subtract 10)

Response:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Message>Inventory updated</Message>
    <Items>
        <Item>
            <SKU>PRODUCT-001</SKU>
            <OldQuantity>100</OldQuantity>
            <NewQuantity>150</NewQuantity>
        </Item>
    </Items>
</Status>

⚠️ Important Notes:

  • Inventory updates should be used sparingly
  • May conflict with receiving activity
  • Large adjustments should be coordinated with EFS
  • Consider using receiving process instead for incoming inventory

Operation 5: Get Shipping Rates

Request shipping rate quotes for potential orders.

Request Type: GetShippingRates

Example:

<?xml version="1.0" encoding="UTF-8"?>
<GetShippingRates>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <DestinationAddress>
        <City>San Francisco</City>
        <State>CA</State>
        <Zip>94102</Zip>
        <Country>US</Country>
    </DestinationAddress>
    
    <Items>
        <Item>
            <SKU>PRODUCT-001</SKU>
            <Quantity>2</Quantity>
        </Item>
    </Items>
</GetShippingRates>

Response:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <ShippingRates>
        <Rate>
            <Method>GROUND</Method>
            <ServiceName>FedEx Ground</ServiceName>
            <Cost>8.50</Cost>
            <TransitDays>3-5</TransitDays>
        </Rate>
        <Rate>
            <Method>PRIORITY</Method>
            <ServiceName>USPS Priority Mail</ServiceName>
            <Cost>12.75</Cost>
            <TransitDays>2-3</TransitDays>
        </Rate>
        <Rate>
            <Method>2DAY</Method>
            <ServiceName>FedEx 2Day</ServiceName>
            <Cost>24.50</Cost>
            <TransitDays>2</TransitDays>
        </Rate>
    </ShippingRates>
</Status>

Use Cases:

  • Display real-time shipping rates at checkout
  • Calculate total order cost
  • Allow customers to choose shipping method
  • Compare shipping costs across methods

Operation 6: Cancel Order

Cancel an order that hasn’t shipped yet.

Request Type: CancelOrder

Example:

<?xml version="1.0" encoding="UTF-8"?>
<CancelOrder>
    <MerchantId>12345</MerchantId>
    <MerchantToken>abc123xyz789</MerchantToken>
    
    <OrderNumber>ORD-2025-001</OrderNumber>
    <Reason>Customer requested cancellation</Reason>
</CancelOrder>

Response – Success:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Success</State>
    <Message>Order canceled successfully</Message>
    <OrderNumber>ORD-2025-001</OrderNumber>
</Status>

Response – Already Shipped:

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Error</State>
    <Message>Order has already shipped and cannot be canceled</Message>
    <OrderNumber>ORD-2025-001</OrderNumber>
    <TrackingNumber>1Z999AA10123456784</TrackingNumber>
</Status>

⚠️ Important:

  • Orders can only be canceled if status is “Pending”
  • Once “Processing” or “Shipped”, cancellation not possible via API
  • Contact support for emergency cancellation of processing orders
  • Cancellation fees may apply per service agreement

5. Shipping Method Codes

Use these codes in the <ShipMethod> tag when submitting orders.

Domestic Shipping Codes

Code Service Typical Transit
GROUND Standard Ground 3-7 business days
Best Rate Economy Lightweight Post Rate shopping – cheapest 3-7 business days
FIRST_CLASS USPS First Class 2-5 business days
PRIORITY USPS Priority Mail 2-3 business days
PRIORITY_EXPRESS USPS Priority Express 1-2 business days
2DAY 2-Day Air 2 business days
OVERNIGHT Next Day Air 1 business day
SATURDAY Saturday Delivery Next Saturday

International Shipping Codes

Code Service Typical Transit
INTERNATIONAL_DEFAULT Standard International 7-21 business days
INTERNATIONAL_PRIORITY Priority International 6-10 business days
INTERNATIONAL_EXPRESS Express International 3-5 business days

Special Codes

Code Purpose
HOLD Hold for pickup (won’t ship)
SHIP_ALONE Ship separately from other orders

Note: Invalid ship method codes will cause order rejection. Contact support if you need a shipping method not listed.

6. Error Handling

HTTP Response Codes

The API endpoint returns HTTP status codes:

HTTP Code

Meaning

Action

200 OK

Request received

Parse XML response for success/error

400 Bad Request

Malformed XML

Check XML syntax

401 Unauthorized

Invalid credentials

Verify MerchantId and MerchantToken

500 Server Error

Server issue

Retry after delay, contact support if persists

503 Service Unavailable

Maintenance

Retry after delay

Common XML Error Codes

Error Code

Message

Solution

AUTH_FAILED

Authentication failed

Check credentials are correct

INVALID_SKU

SKU not found

Add product to FCP or correct SKU

DUPLICATE_ORDER

Order number already exists

Use unique order numbers

INVALID_ADDRESS

Address validation failed

Verify address completeness and accuracy

INVALID_SHIP_METHOD

Ship method not recognized

Use valid shipping code

INSUFFICIENT_INVENTORY

Not enough inventory

Check inventory levels

MALFORMED_XML

XML parsing error

Validate XML syntax

MISSING_REQUIRED_FIELD

Required field missing

Review required fields for request type

Error Response Example

<?xml version="1.0" encoding="UTF-8"?>
<Status>
    <State>Error</State>
    <Message>Invalid SKU: PRODUCT-999 not found in your inventory</Message>
    <ErrorCode>INVALID_SKU</ErrorCode>
    <Field>Items.Item.SKU</Field>
    <Value>PRODUCT-999</Value>
</Status>

Best Practices for Error Handling

1. Parse All Responses

# Pseudocode example
response = send_api_request(xml_data)
xml_doc = parse_xml(response.body)
if xml_doc.status.state == "Error":
    log_error(xml_doc.status.message)
    handle_error(xml_doc.status.error_code)
else:
    process_success(xml_doc)

2. Implement Retry Logic

  • Retry on HTTP 500/503 errors
  • Use exponential backoff (1s, 2s, 4s, 8s)
  • Max 3-5 retry attempts
  • Don’t retry on 400/401 errors (fix request instead)

3. Log All Requests and Responses

  • Log request XML (sanitize credentials)
  • Log response XML
  • Log timestamps and HTTP codes
  • Aids in debugging and support requests

    4. Validate Before Sending

    • Validate XML syntax before sending
    • Check required fields present
    • Verify data types correct
    • Test against schema if available

    5. Handle Timeouts

    • Set reasonable timeout (30-60 seconds)
    • Don’t retry immediately on timeout
    • Log timeout occurrences
    • Contact support if frequent timeouts

    7. Testing Your Integration

    Test Environment

    ⚠️ Important: EFS does not have a separate test/sandbox environment.

    All API calls interact with your live FCP account.

    Testing strategy:

    • Use test products with low inventory value
    • Use unique order numbers (prefix with “TEST-“)
    • Monitor orders closely in FCP
    • Cancel test orders after verification
    • Budget for test order shipping costs

    Testing Checklist

    ☐ Phase 1: Authentication

    • Verify credentials work
    • Test with simple inventory query
    • Confirm XML parsing works
    • Check error handling for bad credentials

    ☐ Phase 2: Read Operations

    • Get inventory for all products
    • Get inventory for specific SKU
    • Query order status (if previous orders exist)
    • Verify response parsing

    ☐ Phase 3: Order Submission

    • Submit test order with valid data
    • Verify order appears in FCP
    • Check order details are correct
    • Test required vs optional fields

    ☐ Phase 4: Error Scenarios

    • Submit order with invalid SKU
    • Submit order with invalid address
    • Submit duplicate order number
    • Submit order with invalid ship method
    • Verify errors handled gracefully

    ☐ Phase 5: Order Tracking

    • Allow test order to ship
    • Query order status
    • Verify tracking number received
    • Test tracking number format parsing

    ☐ Phase 6: Edge Cases

    • Multi-item orders
    • Large quantity orders
    • International addresses
    • Special characters in names/addresses
    • PO Box addresses
    • APO/FPO addresses

    ☐ Phase 7: Performance

    • Test with multiple concurrent requests
    • Measure response times
    • Test under load
    • Verify timeout handling

    Sample Test Orders

    Test Order 1: Simple Domestic

    • 1 SKU, quantity 1
    • Residential address
    • Ground shipping
    • Minimal optional fields

    Test Order 2: Multi-Item

    • 3 different SKUs
    • Various quantities
    • Standard shipping
    • Special instructions included

    Test Order 3: International

    • 1-2 SKUs
    • International address (Canada or UK)
    • International ship method
    • Phone and email included

    Test Order 4: Express Shipping

    • 1 SKU
    • 2DAY or OVERNIGHT method
    • Signature required flag
    • Insurance value included

    Monitoring Live Orders

    After going live:

    Week 1: Monitor Daily

    • Check all orders in FCP
    • Verify order details accurate
    • Confirm tracking updates working
    • Watch for any errors

    Week 2-4: Monitor Regularly

    • Spot-check orders
    • Review error logs
    • Verify inventory accuracy
    • Address any issues promptly

    Ongoing: Automated Monitoring

    • Set up error alerts
    • Monitor API response times
    • Track success/failure rates
    • Log unusual patterns

    8. Best Practices

    Security

    ✓ DO:

    • Store credentials securely (environment variables, secrets manager)
    • Use HTTPS only
    • Sanitize credentials from logs
    • Rotate tokens if compromised
    • Limit credential access to necessary personnel
    • Encrypt credentials at rest

    ✗ DON’T:

    • Hard-code credentials in source code
    • Commit credentials to version control
    • Expose credentials in client-side code
    • Share credentials via email or chat
    • Log full requests including credentials
    • Use HTTP (unencrypted)

    Performance

    Optimize API Usage:

    • Batch requests when possible (e.g., query multiple orders at once)
    • Cache inventory data (don’t query every page load)
    • Use webhooks if available (future feature)
    • Implement rate limiting in your application
    • Schedule intensive operations during off-peak hours

    Rate Limiting:

    • While no hard limits published, be reasonable
    • Don’t exceed 10 requests per second
    • Space out bulk operations
    • Contact support for high-volume needs

    Data Validation

    Before Sending Orders:

    • ✓ Validate address format and completeness
    • ✓ Verify SKUs exist in your FCP account
    • ✓ Check ship method is valid
    • ✓ Ensure quantities are positive integers
    • ✓ Verify order number is unique
    • ✓ Confirm all required fields present

    Before Inventory Updates:

    • ✓ Verify SKU exists
    • ✓ Check quantity is reasonable
    • ✓ Include reason for adjustment
    • ✓ Log all inventory changes

    Error Recovery

    Implement Robust Error Handling:

    • Log all errors with context
    • Retry transient errors (timeouts, 500s)
    • Alert on repeated failures
    • Provide fallback mechanisms
    • Don’t fail silently

    Order Submission Failures:

    • Queue failed orders for retry
    • Alert operations team
    • Consider manual order entry as fallback
    • Don’t lose customer orders

    Logging and Monitoring

    Log:

    • All API requests (sanitized)
    • All API responses
    • Error conditions
    • Performance metrics (response times)
    • Retry attempts

    Monitor:

    • Success/failure rates
    • Response time trends
    • Error frequency
    • Queue depths (if applicable)
    • Inventory sync accuracy

    XML Formatting

    Follow XML Best Practices:

    • Use proper XML declaration
    • Encode special characters (&lt; &gt; &amp; &quot; &apos;)
    • Use UTF-8 encoding
    • Validate XML before sending
    • Use CDATA for long text fields if needed

    Example of Special Character Encoding:

    <Name>John &amp; Jane Smith</Name>  <!-- Correct -->
    <Name>John & Jane Smith</Name>      <!-- Incorrect -->

    Documentation

    Document Your Integration:

    • Map your system fields to API fields
    • Document SKU mapping logic
    • List ship method mappings
    • Record credential locations
    • Note any custom business logic
    • Maintain API version used

    9. Code Examples

    Example 1: Submit Order (Python)

    import requests
    import xml.etree.ElementTree as ET
    from urllib.parse import urlencode
    
    # Configuration
    API_URL = "https://www.efulfillmentservice.com/xml_api_page.php"
    MERCHANT_ID = "12345"
    MERCHANT_TOKEN = "abc123xyz789"
    
    def submit_order(order_data):
        """
        Submit an order to EFS via XML API
        
        Args:
            order_data: Dict containing order information
            
        Returns:
            Dict with success status and EFS order number or error
        """
        
        # Build XML
        root = ET.Element("PostOrders")
        
        # Authentication
        ET.SubElement(root, "MerchantId").text = MERCHANT_ID
        ET.SubElement(root, "MerchantToken").text = MERCHANT_TOKEN
        
        # Order
        order = ET.SubElement(root, "Order")
        ET.SubElement(order, "OrderNumber").text = order_data['order_number']
        ET.SubElement(order, "OrderDate").text = order_data['order_date']
        
        # Shipping Address
        ship_to = ET.SubElement(order, "ShipTo")
        ET.SubElement(ship_to, "Name").text = order_data['ship_name']
        ET.SubElement(ship_to, "Address1").text = order_data['ship_address1']
        if order_data.get('ship_address2'):
            ET.SubElement(ship_to, "Address2").text = order_data['ship_address2']
        ET.SubElement(ship_to, "City").text = order_data['ship_city']
        ET.SubElement(ship_to, "State").text = order_data['ship_state']
        ET.SubElement(ship_to, "Zip").text = order_data['ship_zip']
        ET.SubElement(ship_to, "Country").text = order_data['ship_country']
        ET.SubElement(ship_to, "Phone").text = order_data['ship_phone']
        ET.SubElement(ship_to, "Email").text = order_data['ship_email']
        
        # Ship Method
        ET.SubElement(order, "ShipMethod").text = order_data['ship_method']
        
        # Items
        items = ET.SubElement(order, "Items")
        for item in order_data['items']:
            item_elem = ET.SubElement(items, "Item")
            ET.SubElement(item_elem, "SKU").text = item['sku']
            ET.SubElement(item_elem, "Quantity").text = str(item['quantity'])
        
        # Convert to string
        xml_string = ET.tostring(root, encoding='unicode')
        xml_with_declaration = f'<?xml version="1.0" encoding="UTF-8"?>{xml_string}'
        
        # URL encode
        payload = urlencode({'xml_page': xml_with_declaration})
        
        # Send request
        try:
            response = requests.post(
                API_URL,
                data=payload,
                headers={'Content-Type': 'application/x-www-form-urlencoded'},
                timeout=30
            )
            
            # Parse response
            response_root = ET.fromstring(response.text)
            state = response_root.find('State').text
            message = response_root.find('Message').text
            
            if state == "Success":
                efs_order_num = response_root.find('EFSOrderNumber').text
                return {
                    'success': True,
                    'efs_order_number': efs_order_num,
                    'message': message
                }
            else:
                error_code = response_root.find('ErrorCode')
                return {
                    'success': False,
                    'error': message,
                    'error_code': error_code.text if error_code is not None else None
                }
                
        except requests.exceptions.Timeout:
            return {'success': False, 'error': 'Request timeout'}
        except requests.exceptions.RequestException as e:
            return {'success': False, 'error': str(e)}
        except ET.ParseError as e:
            return {'success': False, 'error': f'XML parse error: {str(e)}'}
    
    
    # Usage example
    order = {
        'order_number': 'ORD-2025-001',
        'order_date': '2025-11-14',
        'ship_name': 'John Smith',
        'ship_address1': '123 Main St',
        'ship_address2': 'Apt 4B',
        'ship_city': 'San Francisco',
        'ship_state': 'CA',
        'ship_zip': '94102',
        'ship_country': 'US',
        'ship_phone': '415-555-1234',
        'ship_email': '[email protected]',
        'ship_method': 'GROUND',
        'items': [
            {'sku': 'PRODUCT-001', 'quantity': 2},
            {'sku': 'PRODUCT-002', 'quantity': 1}
        ]
    }
    
    result = submit_order(order)
    if result['success']:
        print(f"Order submitted successfully: {result['efs_order_number']}")
    else:
        print(f"Order submission failed: {result['error']}")

    Example 2: Get Inventory (PHP)

    <?php
    
    class EFSApi {
        private $apiUrl = 'https://www.efulfillmentservice.com/xml_api_page.php';
        private $merchantId;
        private $merchantToken;
        
        public function __construct($merchantId, $merchantToken) {
            $this->merchantId = $merchantId;
            $this->merchantToken = $merchantToken;
        }
        
        public function getInventory($sku = null) {
            // Build XML
            $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><GetInventory></GetInventory>');
            $xml->addChild('MerchantId', $this->merchantId);
            $xml->addChild('MerchantToken', $this->merchantToken);
            
            if ($sku !== null) {
                $xml->addChild('SKU', $sku);
            }
            
            // Send request
            $response = $this->sendRequest($xml->asXML());
            
            // Parse response
            $responseXml = simplexml_load_string($response);
            
            if ((string)$responseXml->State === 'Success') {
                $inventory = [];
                foreach ($responseXml->Inventory->Item as $item) {
                    $inventory[] = [
                        'sku' => (string)$item->SKU,
                        'description' => (string)$item->Description,
                        'quantity_available' => (int)$item->QuantityAvailable,
                        'quantity_hurt' => (int)$item->QuantityHurt,
                        'quantity_allocated' => (int)$item->QuantityAllocated,
                        'bin_location' => (string)$item->BinLocation
                    ];
                }
                return ['success' => true, 'inventory' => $inventory];
            } else {
                return [
                    'success' => false,
                    'error' => (string)$responseXml->Message,
                    'error_code' => (string)$responseXml->ErrorCode
                ];
            }
        }
        
        private function sendRequest($xmlData) {
            $postData = http_build_query(['xml_page' => $xmlData]);
            
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $this->apiUrl);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/x-www-form-urlencoded'
            ]);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            
            $response = curl_exec($ch);
            
            if (curl_errno($ch)) {
                throw new Exception('cURL error: ' . curl_error($ch));
            }
            
            curl_close($ch);
            
            return $response;
        }
    }
    
    // Usage
    $api = new EFSApi('12345', 'abc123xyz789');
    
    // Get all inventory
    $result = $api->getInventory();
    if ($result['success']) {
        foreach ($result['inventory'] as $item) {
            echo "{$item['sku']}: {$item['quantity_available']} available\n";
        }
    } else {
        echo "Error: {$result['error']}\n";
    }
    
    // Get specific SKU
    $result = $api->getInventory('PRODUCT-001');
    ?>

    Example 3: Get Order Status (Node.js)

    const axios = require('axios');
    const xml2js = require('xml2js');
    
    const API_URL = 'https://www.efulfillmentservice.com/xml_api_page.php';
    const MERCHANT_ID = '12345';
    const MERCHANT_TOKEN = 'abc123xyz789';
    
    async function getOrderStatus(orderNumber) {
        // Build XML
        const builder = new xml2js.Builder({
            xmldec: { version: '1.0', encoding: 'UTF-8' }
        });
        
        const xmlObj = {
            GetOrderStatus: {
                MerchantId: MERCHANT_ID,
                MerchantToken: MERCHANT_TOKEN,
                OrderNumber: orderNumber
            }
        };
        
        const xmlData = builder.buildObject(xmlObj);
        
        try {
            // Send request
            const response = await axios.post(API_URL, 
                `xml_page=${encodeURIComponent(xmlData)}`,
                {
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    timeout: 30000
                }
            );
            
            // Parse response
            const parser = new xml2js.Parser();
            const result = await parser.parseStringPromise(response.data);
            
            if (result.Status.State[0] === 'Success') {
                const order = result.Status.Orders[0].Order[0];
                return {
                    success: true,
                    order: {
                        orderNumber: order.OrderNumber[0],
                        efsOrderNumber: order.EFSOrderNumber[0],
                        status: order.Status[0],
                        trackingNumber: order.TrackingNumber ? order.TrackingNumber[0] : null,
                        carrier: order.Carrier ? order.Carrier[0] : null,
                        shipDate: order.ShipDate ? order.ShipDate[0] : null
                    }
                };
            } else {
                return {
                    success: false,
                    error: result.Status.Message[0],
                    errorCode: result.Status.ErrorCode ? result.Status.ErrorCode[0] : null
                };
            }
            
        } catch (error) {
            return {
                success: false,
                error: error.message
            };
        }
    }
    
    // Usage
    (async () => {
        const result = await getOrderStatus('ORD-2025-001');
        
        if (result.success) {
            console.log('Order Status:', result.order.status);
            if (result.order.trackingNumber) {
                console.log('Tracking:', result.order.trackingNumber);
            }
        } else {
            console.error('Error:', result.error);
        }
    })();

    10. Additional Resources

    Full API Documentation

    For complete technical specifications, detailed field descriptions, and advanced features:

    Contact your Client Care Account Specialist to request access to full API documentation.

    Documentation includes:

    • Complete XML schema definitions
    • All available API operations
    • Advanced features (bundling, kitting, special services)
    • Webhook configurations (if available)
    • Advanced error codes
    • Performance optimization guidelines

    Support Resources

    Technical Support:

    • Submit support ticket: FCP → Client Info → Support
    • Email: [email protected]
    • Phone: 1-866-922-6783 (Press 2)

    For API-Specific Questions:

    • Title support ticket: “API Integration Question”
    • Include code samples and error messages
    • Provide request/response XML (sanitize credentials)

    Client Care Account Specialist:

    • API documentation requests
    • High-volume integration planning
    • Custom feature requests
    • Performance optimization consultation

    Related Guides

    • Bulk Upload CSV Guide – Alternative for manual order imports
    • Platform Integration Guides – Pre-built integrations for common platforms
    • FCP Quick Reference – Understanding FCP functionality
    • Receiving Guide – Product setup requirements

    Development Tools

    Recommended Libraries:

    Python:

    • requests (HTTP client)
    • xml.etree.ElementTree (XML parsing)
    • lxml (advanced XML processing)

    PHP:

    • cURL (HTTP client)
    • SimpleXML (XML parsing)
    • DOMDocument (advanced XML)

    Node.js:

    • axios (HTTP client)
    • xml2js (XML parsing)
    • fast-xml-parser (fast parsing)

    Ruby:

    • HTTParty (HTTP client)
    • Nokogiri (XML parsing)

    Java:

    • Apache HttpClient (HTTP)
    • JAXB (XML binding)
    • DOM/SAX parsers

    XML Validators

    Test your XML before sending:

    • XMLLint (command-line)
    • Online validators (search “XML validator”)
    • IDE plugins (VSCode XML extension, IntelliJ XML support)

    11. Frequently Asked Questions

    Q: Is there a sandbox/test environment? A: No, all API calls interact with your live account. Use test products and monitor orders carefully during development.

    Q: What’s the API rate limit? A: While no hard limit is published, keep requests under 10 per second. Contact support for high-volume needs.

    Q: Can I get webhooks for order updates? A: Webhook functionality may be available. Contact your Client Care Account Specialist to discuss.

    Q: How quickly are orders processed after submission? A: Orders typically ship within 1 business day of receipt. API submission is immediate.

    Q: Can I update orders after submission? A: Limited updates possible via API (cancel if pending). For other changes, contact support immediately.

    Q: What if my platform isn’t in your pre-built integrations? A: Use the XML API to build a custom integration or use Bulk CSV Upload as an alternative.

    Q: Can I submit orders with multiple ship-to addresses? A: No, each order must have single ship-to address. Submit separate orders for multiple destinations.

    Q: How do I handle split shipments? A: If an order ships in multiple packages, you’ll receive multiple tracking numbers. Query order status to get all tracking.

    Q: Can I white-label the API (use for my own clients)? A: Contact your Client Care Account Specialist to discuss reseller or white-label arrangements.

    Q: What if I need help with my integration? A: Submit a support ticket with details about your integration, including code samples and error messages.

    12. Next Steps

    To begin your API integration:

    1. Gather credentials from FCP (Merchant ID and Token)
    2. Review code examples in your preferred language
    3. Set up development environment with secure credential storage
    4. Test authentication with simple inventory query
    5. Submit test orders and verify in FCP
    6. Implement error handling and logging
    7. Test edge cases and error scenarios
    8. Monitor live orders closely for first week
    9. Request full API documentation if needed for advanced features

    For assistance at any stage, contact your Client Care Account Specialist or submit a support ticket.