At Last Integration

At Last Integration

Version Created / Updated by Date Notes
1.0 Danail Deltchev 26.09.2023 First publish
1.1 Milen Markov 22.01.2024 Introduced ‘Use SMSRN'’ and “Order Number Prefix” fields

The purpose of this document is to explain what is “At Last” and why and how we are to integrate to it

At Last is a Returns software solution that takes care of data standardisation, communication with couriers for return labels provision and custom UI portals building, branded for their clients. The main idea is for a seller to embed the At Last Returns portal to their website, marketplace store or whatever else needed, feed them with the order data and this way provide their buyers with an easy option to find their order and request a return label for it when needed. As we are the source of marketplace orders for the different models of our partners it makes best sense for us to actually feed the information to At Last for our clients' end buyers to be able to request returns.

Essentially this functionality will be a simple order export from Hemi to an endpoint in the specific format. The only complication for us is to make sure we have the details set the right way so the model can work easily for both single seller and multi seller instances (or generally speaking MS vs Arcade)

Having in mind the idea is for a portal where the end BUYER is the most important one though this means we shouldn’t be thinking of splitting an order by seller as for the buyer when they place an order on 1 account they see it as one order

Based on the above two leading statements we will pick the option to set AtLast portals and make our exports work per Hemi Account.

Authorisation

The Authorisation for AtLast is simple - we need to provide an API key as a header as x-api-key

The only other thing that is mandatory for that part is a header 'Content-Type: application/json'

The x-api-key should be kept in a new section for the Account - Account At Last with the field name “API Key”

To control the At Last functionality we should first look at Account > Active. If it is Yes we should also look at the Account At Last > Active flag too (new field to be introduced). If Yes then we should export the orders for this Account. If any of these are “No” we should just skip this account

The call itself is a standard Post call to url in the likes of: https://ingest-dev.atlast.co/orders (this is the sandbox endpoint that can be used, the Key to use is in the curl file below). We should keep the url/endpoint in another field in Account At Last called “Endpoint”

Payload

Below can be found an example payload for the order sending, a JSON schema that can be used for any validations and of course our mapping followed by any additional specifications

order-curl.txt

OrderIngestModel.json

Example payload:

[
    {
        "id": "8f025561-1123-461a-b977-81194942a6f7",
        "merchantId": "9254353b-13fe-403c-bcd5-a172f1f08641",
        "createdAt": "2023-08-06T16:32:59.866Z",
        "orderNumber": "henry-111",
        "orderTotal": {
            "value": 200,
            "currency": "GBP"
        },
        "itemSubTotal": {
            "value": 200,
            "currency": "GBP"
        },
        "shippingTotal": {
            "value": 1,
            "currency": "GBP"
        },
        "discount": {
            "value": 0,
            "currency": "GBP"
        },
        "items": [
            {
                "id": "123",
                "sku": "ad98as89d87asd98",
                "quantity": 1,
                "name": "Henry Hoover",
                "description": "hoover",
                "imageUrl": "https://www.costco.co.uk/medias/sys_master/images/h6a/h51/86640987340830.jpg",
                "price": {
                    "value": 200,
                    "currency": "GBP"
                },
                "discount": {
                    "value": 0,
                    "currency": "GBP"
                },
                "weight": 1
            }
        ],
        "customer": {
            "firstName": "rob",
            "lastName": "sou",
            "phone": "121212",
            "address": {
                "line1": "some street",
                "city": "London",
                "postCode": "nw1902",
                "country": "GB"
            }
        }
    }
]

Example Mapping:

At Last Field At Last Requirement Hemi Mapping Hemi Notes
id Y Account At Last > Order Prefix

+ Orders > ID | Concatenated Account At Last > Order Prefix is a new field to be added and if it is empty we simply send only our Orders > ID | | merchantId | | | Y | Account At Last > Merchant ID | New field | | createdAt | | | Y | Orders > Order Created Time | Formatted as per the example Payload | | orderNumber | | | Y | Account At Last > Order Number Prefix + Orders > Marketplace Order ID OR Account At Last > Order Number Prefix + Orders > Selling Manager SalesRecordNumber | <v1.1>If Account At Last > Use SMSRN is '0', we pick Orders > Marketplace Order ID If Account At Last > Use SMSRN is '1', we pick Orders > Selling Manager SalesRecordNumber If Account At Last > Order Number Prefix is empty, we send only the respective ID. </v1.1> | | reference | | | N | Account At Last > Order Prefix + Orders > ID | This field is not in the example payload and not in the schema as it was added as something new specifically for Warehow and Arcade. Value should be the same as id | | orderTotal | | | Y | | | | | value | | Y | Orders > Order Total Amount | | | | currency | | Y | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | itemSubTotal | | | Y | | | | | value | | Y | Orders > Order Subtotal Amount | | | | currency | | Y | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | shippingTotal | | | Y | | | | | value | | Y | Orders > Shipping Service Cost | | | | currency | | Y | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | discount | | | N | | | | | value | | N | Orders > Discount Value | | | | currency | | N | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | items | | | Y | | | | | id | | Y | Product in Order > ID | | | | sku | | N | Product in Order > SKU | | | | quantity | | Y | Product in Order > Quantity | | | | name | | N | Product in Order > Item Title | | | | description | | N | Product in Order > Item Title | | | | imageUrl | | N | N/A | | | | price | | Y | | | | | | value | Y | Product in Order > Item Price | | | | | currency | Y | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | | discount | | N | | | | | | value | N | Product in Order > Discount Amount | | | | | currency | N | Orders > Currency | In the odd case there is no currency use the Account > Exchange Rate Calculator Currency as a default | | | weight | | N | Product in Order > Weight | If the product in Hemi doesn’t have a value or that value is a 0 or a negative number (in other words anything different than a positive number) don’t send this field at all | | customer | | | | | | | | firstName | | N | Orders > Shipping Buyer Name | Split the string on the first space. Everything before the first space should go in “firstName” everything else should go in “lastName”. If there are no spaces in the string duplicate the info in both fields | | | lastName | | Y | Orders > Shipping Buyer Name | Split the string on the first space. Everything before the first space should go in “firstName” everything else should go in “lastName”. If there are no spaces in the string duplicate the info in both fields | | | phone | | N | Orders > Shipping Phone | If the field in Hemi is empty don’t send this field | | | address | | Y | | | | | | line1 | Y | Orders > Shipping Street 1 | If Street1 in Hemi is empty use Street2 here and don’t add it for line2. If both are empty this should return an error for missing mandatory field | | | | line2 | N | Orders > Shipping Street 2 | This is not provided in the example payload. If empty in Hemi don’t send the field at all | | | | city | Y | Orders > Shipping City | | | | | postCode | Y | Orders > Shipping Postal Code | | | | | country | Y | Orders > Shipping Country Code | If Shipping Country Code is empty use the Shipping Country Name to map the code via our standard mapping. If we can’t find the code this should return an error for missing mandatory field |

The fields marked as “At Last Requirement” should always be filled in. If we don’t have the information to fill in we should block the order and set an error in the Order Error for missing mandatory data. Everything else that is NOT mandatory if we don’t have the data (example - Item Title) we should not send the field at all. For any errors connected to At Last we should use the type “Connector”

As the payload shows at the moment we can send multiple orders per payload. The response will either be just blank 200/204 OK if everything is fine or if there is any error it will be based on the index of the call (each object in the payload, starting from 0). Below you can see the example error response when sending 2 orders and the first was successful (index 0) and the second one was not (index 1)

[
    {
        "index": 1,
        "errors": {
            "_errors": [],
            "itemSubTotal": {
                "_errors": [
                    "Required"
                ]
            }
        }
    }
]

We can send up to 100 orders per call and 1 call per 10 seconds

Based on the above description of indexing we can easily track order by order if it was success or not. If there was an error for any order we should store it in Order Error and write the relevant message. As it will take too much time to parse the error we should just store everything that we see in the “errors” object for the relevant “index”. In other words from the above example for the order with index 1 in the payload we should directly store the following error message:

{
            "_errors": [],
            "itemSubTotal": {
                "_errors": [
                    "Required"
                ]
            }
        }

If it was success or error we should also track this on every order to ensure we don’t send orders again and again. I suggest we keep this in a new section Order At Last where we store the status of the order export and we can use - empty or Pending Status (empty as there will be no record when we first pick it up, Pending as it might need to be resent) for pickup, Error when there is a problem, internal or external, and Completed when successfully sent.

The other rules for sending are:

  • Any order that is not on Pending, Incomplete or Cancelled status to be sent
  • Any order that has Orders > Order created time bigger than Account At Last > Start Date (new field to control from when orders should start flowing into At Last system for said account so we can limit sending older Shipped orders. The field should be of type “Date picker” as we just care from which day onwards we should start sending the info). If there is no “Start Date” set we treat it as all orders should be sent that fit the other criteria
Is this article helpful?
0 0 0