Poisson Aerohead
Poisson Aerohead

Reputation: 360

UPS API for SurePost

I am building a shipping cost minimizer for a client. I have a list of shipping options from various vendors (USPS, UPS) and I have them all working except UPS SurePost. The client does have SurePost set up for the account. There is a thread here where someone put an XML example. I also have the supplemental documentation.

Has anyone out there managed to get a successful API call in JSON format for a SurePost Get Rates? I tried both changing over a working UPS Ground call (as shown below) and replicating the example XML call from the supplemental documentation in JSON format. Neither has worked, and in fact, my response from UPS is not even some XML style error message, but rather a garbled mess (as shown below).

Please see my example API data package. Note that when Service '03' 'Ground' is uncommented, this is a proven working API call. When I change it to '93' (For SurePost 1 lb or Greater) and the Description 'Parcel Select' (which came from the supplemental documentation), I get a response code 400 and a garbled mess.

let data = JSON.stringify({
                            "RateRequest":{
                              "Request":{
                                "SubVersion":"1703",
                                "TransactionReference":{
                                  "CustomerContext":" "
                                }
                              },
                              "Shipment":{
                                "ShipmentRatingOptions":{
                                  "UserLevelDiscountIndicator":"TRUE"
                                },
                                "Shipper":{
                                  "Name":"Billy Blanks",
                                  "ShipperNumber":" ",
                                  "Address":{
                                    "AddressLine":"366 Robin LN SE",
                                    "City":"Marietta",
                                    "StateProvinceCode":"GA",
                                    "PostalCode":"30067",
                                    "CountryCode":"US"
                                  }
                                },
                                "ShipTo":{
                                  "Name":"Sarita Lynn",
                                  "Address":{
                                    "AddressLine":"355 West San Fernando Street",
                                    "City":"San Jose",
                                    "StateProvinceCode":"CA",
                                    "PostalCode":"95113",
                                    "CountryCode":"US"
                                  }
                                },
                                "ShipFrom":{
                                  "Name":"Billy Blanks",
                                  "Address":{
                                    "AddressLine":"366 Robin LN SE",
                                    "City":"Marietta",
                                    "StateProvinceCode":"GA",
                                    "PostalCode":"30067",
                                    "CountryCode":"US"
                                  }
                                },
                                "Service":{
                                  // Uncomment the desired service
                                  //"Code" : "03",
                                  //"Description" : "Ground"
                                  "Code":"93",
                                  "Description" : "Parcel Select"
                                },
                                "Package":{
                                  "PackagingType":{
                                    "Code":"02",
                                    "Description":"Package"
                                  },
                                  "Dimensions":{
                                    "UnitOfMeasurement":{
                                      "Code":"IN"
                                    },
                                    "Length":"7",
                                    "Width":"7",
                                    "Height":"6"
                                  },
                                  "PackageWeight":{
                                    "UnitOfMeasurement":{
                                      "Code":"LBS"
                                    },
                                    "Weight":"7"
                                  }
                                }
                              }
                            }
                          });

let headers = {
                'AccessLicenseNumber' : 'MYLICENSENUM',
                'Password' : 'MYPASSWORD',
                'Content-Type' : 'application/json',
                'Content-Length' : data.length,
                'transID' : 'Trans' + Date.now().toString(),
                'transactionSrc' : 'node',
                'Username' : 'MYUSERNAME',
                'Accept' : '*/*'
              };

let options = {
                'hostname' : 'wwwcie.ups.com',
                'path' : '/ship/v1/rating/Rate',
                'method' : 'POST',
                'headers' : headers
              };

The request is sent as a node https.request for these options.

For a SurePost call, the response code is 400 and the message is garbled, as I said. Please see this image, which includes both a toString('hex') to explicitly write the byte values returned, as well as a plain toSting() to attempt a human readable print.

Console Log of API Response - Status Code, Hex Values, Characters

Does anyone have any input on this error and what might be malformed? All the supplemental docs say is required is the Service Code 93 (or 92 for less than 1 lb). So, since this works for Service Code 03 UPS Ground, I would expect this to work, at least well enough to give me a valid error message explaining anything else needed.

Upvotes: 0

Views: 2221

Answers (2)

Will Belden
Will Belden

Reputation: 658

This section of a request did work. If you get "...one unit of measurement" error, it's the 92/93 service code. 92 for OZS and .1 - 15.99 ounces. 93 for 1 lb and above.

"Service": {
      "Code": "92",
      "Description": "SurePost"
    },
    "Package": {
      "Description": " ",
      "Packaging": {
        "Code": "02",
        "Description": "Nails"
      },
      "Dimensions": {
        "UnitOfMeasurement": {
          "Code": "IN",
          "Description": "Inches"
        },
        "Length": "10",
        "Width": "30",
        "Height": "45"
      },
      "PackageWeight": {
        "UnitOfMeasurement": {
          "Code": "OZS",
          "Description": "Ounces"
        },
        "Weight": "14"
      }

Upvotes: 1

Poisson Aerohead
Poisson Aerohead

Reputation: 360

I was able to get it to work. First, I found from the unrelated question here that there is a /json/ route, so I guessed (correctly) that I could use /json/Rate. Additionally, in order to participate in SurePost, I could not use the fake Shipper data from the sample examples, since an account must set up SurePost. Complete (sanitized) solution below:

let data = JSON.stringify({
                            "Security": {
                              "UsernameToken": {
                                "Username": "MYUSERNAME",
                                "Password": "MYPASSWORD"
                              },
                              "UPSServiceAccessToken": {
                                "AccessLicenseNumber": "MYACCESSLICENSENUMBER"
                              }
                            },
                            "RateRequest":{
                              "Request":{
                                "SubVersion":"2201",
                                "RequestOption" : "Rate",
                                "TransactionReference":{
                                  "CustomerContext":" "
                                }
                              },
                              "Shipment":{
                                "ShipmentRatingOptions":{
                                  "UserLevelDiscountIndicator":"TRUE"
                                },
                                "Shipper":{
                                  "Name":"MYCOMPANYNAME",
                                  "ShipperNumber":"MYSIXDIGITNUMBER",
                                  "Address":{
                                    "AddressLine":"MYCOMPANYADDRESS",
                                    "City":"MYCITY",
                                    "StateProvinceCode":"MYSTATE",
                                    "PostalCode":"MYZIP",
                                    "CountryCode":"US"
                                  }
                                },
                                "ShipTo":{
                                  "Name":"Sarita Lynn",
                                  "Address":{
                                    "AddressLine":"355 West San Fernando Street",
                                    "City":"San Jose",
                                    "StateProvinceCode":"CA",
                                    "PostalCode":"95113",
                                    "CountryCode":"US"
                                  }
                                },
                                "ShipFrom":{
                                  "Name":"Billy Blanks",
                                  "Address":{
                                    "AddressLine":"366 Robin LN SE",
                                    "City":"Marietta",
                                    "StateProvinceCode":"GA",
                                    "PostalCode":"30067",
                                    "CountryCode":"US"
                                  }
                                },
                                "Service":{
                                  "Code":"93",
                                  "Description":"Parcel Select"
                                },
                                "Package":{
                                  "PackagingType":{
                                    "Code":"02",
                                    "Description":"Package"
                                  },
                                  "Dimensions":{
                                    "UnitOfMeasurement":{
                                      "Code":"IN"
                                    },
                                    "Length":"10",
                                    "Width":"7",
                                    "Height":"5"
                                  },
                                  "PackageWeight":{
                                    "UnitOfMeasurement":{
                                      "Code":"LBS"
                                    },
                                    "Weight":"7"
                                  }
                                }
                              }
                            }
                          });

let headers = {
                'Content-Type' : 'application/json',
                'Content-Length' : data.length,
                'Accept' : '*/*'
              };

// wwwcie - Customer Integration
// onlinetools - Production
let options = {
                'hostname' : 'wwwcie.ups.com',
                'path' : '/json/Rate',
                'method' : 'POST',
                'headers' : headers
              };

let result = "";

async function api_call(){

    let req = https.request(options, res => {
                                              console.log(`statusCode: ${res.statusCode}`);
                                              res.on('data', (d) => { result += d.toString(); });
                                            });



    req.on('error', error => {
        console.error(error)
    });

    await req.write(data);
    await req.end();

}

EDIT: As a final 'gotcha', when doing SurePost less than 1 lb (Code '92'), be sure to use "Code":"OZS" for the 'PackageWeight'.'UnitOfMeasurement'. Note that 'OZ' will not work and the error message does not instruct you to use 'OZS'. For me, when using 'LBS' it just said the unit of measurement was wrong, and when using 'OZ' it said to use only 'LBS' or 'KGS'! Documentation and error messages that leave a great deal to be desired...

Upvotes: 0

Related Questions