Best Practises

Table of Contents:

  1. Introduction
  2. Device Identification
  3. Device Condition & Price Lookup
  4. Due Diligence Checks
  5. Trade-In Registration
  6. Temporary Trade-In Registration
  7. Custom Data Fields & Validation
  8. Campaigns
  9. Shipping Functions
  10. Users & Locations
  11. PDF Signing
  12. Error Messages

1. Introduction

The Foxway Vendor API is a traditional RESTful API, designed for ease of implementation and understanding.

Key takeaways:

  • All timestamps are provided in UTC format
  • API employs basic authentication (x-apikey), which is obtainable from your Account Manager
    • The production base URL, along with the API key, will be provided by the technical contact assigned to you
  • Both development and production environments are supported
  • Comprehensive Swagger documentation can is available here.

This document aims to outline the best practices for integrating our API into:

  • Your website, to enable customers to quickly and easily determine the value of their devices
  • Your Point of Sale systems, for seamless integration of trade-ins with your existing sales processes
  • Your Online Store, allowing customers to directly deduct the trade-in value from their shopping cart
  • A combination of the above, tailored to your specific requirements

 

2. Device Identification

Our API provides device identification by:
IMEI – for mobiles & tablets
Serial number – for Apple products
Search string (like: “Macbook Pro”, “iphone xs max”)

 

 

Screenshot 2024-06-06 at 16.57.33.png Most common approach in the webpage is to provide one textbox type search field which will display results from GET /api/v2/retail/models

 

Alternative approach - product tree as Item Groups & Manufacturers


You can show customers a list of available Item Groups and then a list of all available Manufacturers for the given Item Group. This can be achieved via calls GET /api/v2/retail/itemgroups & GET /api/v2/retail/manufacturers which are described further in Swagger.

To get results either by IMEI, serial number or search string only one API call should be used: GET /api/v2/retail/models. This call will return all possible models.

Parameter term should be used to narrow down the results. If you’ve chosen the alternative approach, then you can also specify parameters ItemGroupId and ManufacturerId.

By default, GET /api/v2/retail/models call returns 500 results. While this is fine for one search box option, then for the product tree approach this limit should be increased to x so that all products belonging to certain Item Group and Manufacturer would be shown. This can be achieved by using "limit" parameter within the call (GET api/v2/retail/models?limit=x)

 

Example request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/models?term=iphone%20xs%20max&api-version=2.0

Example response:

{
"Data": [
{
"Id": 19849, //this is Model Id and needs to be stored for further calls
"ItemVariantType": 1,
"ItemId": 19770,
"ItemName": "iPhone Xs Max",
"ProductName": "Apple iPhone Xs Max 512GB",
"ProductCode": "MT562QN/A,MT572QN/A,MT582QN/A,MT512B/A",
"ProductNameShort": "Apple iPhone Xs Max 512GB",
"ItemGroupId": 1,
"ManufacturerId": 116,
"Price": 234, // this is the price that can be shown to customer for a Working device
"CurrencyIsoCode": "EUR",
"CurrencySymbol": "€",
"ExchangeRate": 1,
"PictureUrl": https://dcosix8as1189.cloudfront.net/imagestorage/ItemsPicture/iPhone%20XS%20Max.png //can be used to show image of the device
},
{
"Id": 19848,
"ItemVariantType": 1,
"ItemId": 19770,
"ItemName": "iPhone Xs Max",
"ProductName": "Apple iPhone Xs Max 256GB",
"ProductCode": "MT532QN/A,MT542QN/A,MT552QN/A,MT512B/A",
"ProductNameShort": "Apple iPhone Xs Max 256GB",
"ItemGroupId": 1,
"ManufacturerId": 116,
"Price": 221,
"CurrencyIsoCode": "EUR",
"CurrencySymbol": "€",
"ExchangeRate": 1,
"PictureUrl": "https://dcosix8as1189.cloudfront.net/imagestorage/ItemsPicture/iPhone%20XS%20Max.png"
},
{
"Id": 19847,
"ItemVariantType": 1,
"ItemId": 19770,
"ItemName": "iPhone Xs Max",
"ProductName": "Apple iPhone Xs Max 64GB",
"ProductCode": "MT502QN/A,MT512QN/A,MT522QN/A,MT512B/A",
"ProductNameShort": "Apple iPhone Xs Max 64GB",
"ItemGroupId": 1,
"ManufacturerId": 116,
"Price": 203,
"CurrencyIsoCode": "EUR",
"CurrencySymbol": "€",
"ExchangeRate": 1,
"PictureUrl": "https://dcosix8as1189.cloudfront.net/imagestorage/ItemsPicture/iPhone%20XS%20Max.png"
}
]
}

 

3. Device Condition & Price Lookup

If customer selects one of the options then the next 2 calls should be made.

First call should be the GET /api/v2/retail/models/{id}/grading-form which returns the questions about device condition which need to be answered (we call it “grading form”). {id} is the Model Id stored from previous call.

PS! Foxway handles translations – if you need multi-language support we need to enable the required languages from our end and you can send the required language code via the header!

 

Example request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/models/19849/grading-form?api-version=2.0

Example response:

{
"Id": 8,
"Name": "!Mobiles / Tablets / iPods (No Premium)",
"Questions": [
{
"Id": 0, // Question ID, needs to be used when submitting answers
"Name": "Does the device turn on and stay on without a charger?", //Question itself which should be shown to customer
"Label": "Power", //Question „Label“ – useful when showing quick summary of questions-answers at later step
"Info": "<p>&nbsp;</p>",
"InfoCards": [ //Infocards are kind of like a helpful guide for correctly answering the questions. These are not required, but recommended as per best
practices to reduce grading discrepancies. There are also images available for further clarification under some questions.
{
"Id": 0,
"Label": "Info card 209",
"Text": "<p><strong>All criteria must be met before selecting &quot;YES&quot;!</strong></p><ul><li>Device can be turned on and off</li><li>Device
reaches home screen or setup menu</li><li>Device is working without charger</li></ul><p><br></p>",
"Images": []
}
],
"Answers": [ //These are the available answers to the questions. We have usually set these up as a simple Yes/No choices when submitting back the
answers at a later step. However there can be exceptions for certain item groups so should always be checked what is available. Also keep in mind tha „0“ is
NOT always „Yes“ as we have changed the order of the answers for some quesions to be more user-friendly, i.e. „Is the device in perfect condition“ we’ve
set up so that „No“ is the first choice (and therefor with ID=0) as most traded in devices are actually not in perfect condition.
{
"Id": 0,
"Name": "Yes",
"Score": 100,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
},
{
"Id": 1,
"Name": "No",
"Score": 5,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
}
]
},
{
"Id": 1,
"Name": "Is LCD working and without physical damage?",
"Label": "LCD",
"Info": "<p>&nbsp;</p>",
"InfoCards": [
{
"Id": 0,
"Label": "Info card 213",
"Text": "<p><strong>All criteria must be met before selecting &quot;YES&quot;</strong></p><ul><li>LCD is without any clear signs of discoloration
(screenburn)</li><li>Touchscreen is fully working (check edges of the screen)</li><li>Glass can be cracked</li></ul>",
"Images": [
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/adc9acd0-c84a-4a73-a7c9-2299e3cbb502.png",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/706b70cf-78fe-4644-bb9b-e546733535e3.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/d62b5aae-5c92-4da9-b733-cc07f1ce394f.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/38c1fd2b-66ae-421b-bf95-71d622e8d9ee.jpg"
]
}
],
"Answers": [
{
"Id": 0,
"Name": "Yes",
"Score": 100,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
},
{
"Id": 1,
"Name": "No",
"Score": 30,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
}
]
},
{
"Id": 2,
"Name": "Are front, frame and back cover in good condition (general wear is acceptable)?",
"Label": "Body",
"Info": "<p>&nbsp;</p>",
"InfoCards": [
{
"Id": 0,
"Label": "Info card 218",
"Text": "<p><strong>All criteria must be met before selecting &quot;YES&quot;</strong></p><ul><li>Display glass is without cracks or chips on
edges</li><li>There are no cracks, splits or fractures on the frame or back cover</li><li>The frame of the device is not bent</li><li>Camera glass is not
cracked</li><li>Device is complete, no parts missing (battery, backcover, port cover, screws)</li><li>Screen is not visibly loose (can see under the
screen)</li><li>No apparent modifications have been used</li></ul>",
"Images": [
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/broken%20home%20button%20finalmin__1532938672_213.168.11.178.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/9fbc5fb7-0621-4f55-83a0-cb1af80301d6.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/ccd493c8-cf93-4ca0-899e-f01494800c87.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/b013ff5a-7fb5-4d4e-8a21-5d7ee8e88528.jpg",
"https://dcosix8as1189.cloudfront.net/imagestorage/GradingFormInfoCardImage/6e31d4ac-3d4e-434a-b79f-0ee0812d054a.jpg"
]
}
],
"Answers": [
{
"Id": 0,
"Name": "Yes",
"Score": 100,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
},
{
"Id": 1,
"Name": "No",
"Score": 70,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
}
]
},
{
"Id": 3,
"Name": "Are all buttons present and working?",
"Label": "Buttons",
"Info": "<p>&nbsp;</p>",
"InfoCards": [
{
"Id": 0,
"Label": "Info card 226",
"Text": "<p><strong>All criteria must be met before selecting &quot;YES&quot;</strong></p><ul><li>All buttons work easily (Home, Power, Volume
and Mute) without excessive force</li></ul>",
"Images": []
}
],
"Answers": [
{
"Id": 0,
"Name": "Yes",
"Score": 100,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
},
{
"Id": 1,
"Name": "No",
"Score": 80,
"InfoCards": [],
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
}
]
}
],
"Extras": [], //this is relevant for some Item Groups, i.e. for laptops we ask if a working power adapter is included. ID of this Extra would be shown here and
in case of positive answer it can be submitted back using the ID and „1“ for Yes. If the answer would be „No“, then it is not needed to use this option.
Considering the nature of these extras, we suggest to show them as tick boxes.
"SystemExtras": [
{
"Id": 1,
"IsVendorSelected": false,
"IsPartnerSelected": false,
"IsAffiliateSelected": false
}
],
"ScoringType": 2
}

From this response the important part to display to customer are the questions and the answers: Questions.Name and Answers.Name. Answers should be displayed as radio buttons because the idea is to let customer select only one answer per question.

Immediately after the grading form is requested the next calls should be POST /api/v2/retail/price-quote where all the answers should be sent as answer ID-s. From the response the device price will be revealed and customer can see the grading form.

We suggest to post the price-quote originally with all questions answered as “Yes” (Answer Id needs to be checked – for some questions we have changed the order of answers so it can either be “0” or “1”) to show customer the max price for a fully working device in the best possible condition.

Previously we stated you can also get the device “fully working value” directly from the GET Models query, but the difference is that via using Grading Form there might questions which actually increase the base price (i.e. Is the device in perfect cosmetic condition?). Upon each change in answers the grading form should be sent back using POST /api/v2/retail/price-quote with proper answers.

 

Here is one example how the call should look like what is sent to API:

{
"GradingForm": {
"Answers": {0:[0], 1:[0], 2:[0], 3:[0]},
"Extras": [ //Extras from grading form, simple array of Extras IDs that are answered "Yes" (comma separated in case of multiple Extras, blank if no Extras are present)
0
],
"SystemExtras": [ //this is used for posting DueDiligence results, described at a later part of this document. At this stage of just getting a price of the
device it is not yet important.
0
],
"GradingDifferenceImages": {}
},
"ModelId": 19849,
"ItemIdentifier": null,
"LocationId": null,
"AgentId": null,
"TradeInChannel": 0,
"TradeInType": 0,
"CampaignId": 0 //used for applying campaign. Described at a later part of this document.
}

Please note that the red part of the request needs to be added to the grading form response. ItemIdentifier should be real one if the identifier is known – if not, then it can be also either empty or NULL. LocationId and AgentId must be null in case of web, because there are none in place at this time. For in Store use these should be sent back with proper values for correct tracking. This will be described at a later part of this document under Users & Locations. TradeInType should be 0 in most scenarios. TradeInChannel is 0 for Web, but these should be clarified with Foxway at the implementation phase.

Response example for the above request:

{
"PriceQuoteId": "806063fb-fd9d-49ba-86ec-0dc6e5ed067a", //should be stored for use in further stages
"ItemIdentifier": null,
"Grade": "Working", //answers to grading form questions translated to a Condition. Also translated as per language requested via header.
"OfferedPrice": 1481, //value to be shown to customer
"OfferedPriceInclVat": 1481,
"VATCoefficient": null,
"CurrencyIsoCode": "AED",
,"د.إ" :"CurrencySymbol "
"ExchangeRate": 4.126, //Currency & Exchange rate info when any currency beside EUR is used – Foxway base currency is EUR.
"TradeInChannel": 0,
"TradeInType": 0,
"PotentialCampaign": { //If campaign was applied and there are bonuses applied, these will be shown here.
"DeviceBonusInclVat": 0,
"DealBonusInclVat": 0,
"CampaignId": 0
}

You need to show out the OfferedPrice value to customer. And the request price-quote should be sent to API each time the customer changes the options and new price should be shown.

4. Due Diligence Checks

If proper price-quote is done and customer is happy with the price, then following requests need to be made in exact order:

Due Diligence check – POST /api/v2/retail/duediligence/check

This call performs all kind of needed checks – iCloud lock, Checkmend and so on.

Request:

{
"ModelId": 5525,
"ItemIdentifier": "355358080543351"
}

Response:

"2969c20c-8452-4195-861d-c5dd3a638f64" //Report Id which needs to be stored to find out the actual results behind it


To get the details behind the due diligence check following endpoint needs to be requested:

https://api.foxway.tech/api/v2/retail/duediligence/report/{reportId}


Response:

{
"FraudCheckResults": [
{
"Provider": "CheckmendDeviceBlackListProvider",
"CertId": "8-E498F1282F5-465:812B211F",
"Result": "Passed",
"ResultId": 1,
"Timestamp": "2019-10-12T19:46:16.0257285"
}
],
"FmipCheckResults": [
{
"Provider": "CheckmendFmipProvider",
"CertId": "8-E498F129544-575:238C7DA6",
"Result": "Activated",
"ResultId": 1,
"Timestamp": "2019-10-12T19:46:16.8850625"
}
]
}
FraudCheck FMIP Check
Passed (ResultId=1)

Everything is good

Activated (ResultId=1) Send back SystemExtra=1 in PriceQuote. Price will be reduced
Failed (ResultId=2) Do not allow to proceed with trade-in Not activated (ResultId=2) Send back SystemExtra=0 in PriceQuote
    Not Applicable (ResultId=3) Not applicable to selected product, usable for Apple products only


From this response is visible if the iCloud lock is enabled on the Apple devices or not and also if the device is reported lost or stolen. After acquiring DueDiligence results, please post a new PriceQuote with proper SystemExtra value. Keep in mind blocking the trade-in flow in case of “Failed” FraudCheck result should also be implemented on your side.

5. Trade-In Registration

If the DueDiligence check is made, then the trade-in needs to be registered using POST /api/v2/retail/tradeins where PriceQuoteId needs to be from the last accepted pricequote.

Request:

{
"TradeInDevices": [
{
"PriceQuoteId": "95B9C470-9502-45E9-9B86-FC0170DA9388",
"DeviceIdentifier": "355358080543351"
}
],
"CustomDataFields": [ // These are the specific data fields configured for specific setup to be used during trade-in registration. We will cover these in the
next section
{
"DataFieldId": 9,
"DataFieldValue": "Salesperson Name"
},
{
"DataFieldId": 10,
"DataFieldValue": "Customer Address"
}
],
"IsCorporate": true, // this used used to specify the customer type. It is important to properly handle this in cases where we have agreed on a Marginal VAT
financial setup – this means if customer is a private consumer (IsCorporate=False), then we by default handle this transaction as a Marginal VAT type of a
transaction
"OrganizationName": "Name or the Company",
"ClientFirstName": "First Name",
"ClientLastName": "Last Name",
"AgentId": null,
"AgentRef": "Client side reference",
"LocationId": null,
"LocationRef": "Client side reference",
"TradeInRef": "Client side reference"
}


Response:

{
"Id": 101386,
"CreatedDate": "2019-10-12T20:02:40.3378067",
"TradeInDevices": [
{
"TradeInDeviceId": 106752,
"DeviceIdentifier": "355358080543351",
"PriceQuoteId": "95b9c470-9502-45e9-9b86-fc0170da9388",
"ProductName": "Apple iPhone 7 Plus 128GB",
"Grade": "Working",
"Price": 333,
"CurrencyIsoCode": "AED",
,"د.إ" :"CurrencySymbol "
"ExchangeRate": 4.126
}
],
"Status": "Registered",
"CustomDataFields": [
{
"DataFieldId": 9,
"DataFieldValue": "Salesperson Name"
},
{
"DataFieldId": 10,
"DataFieldValue": "Customer Address"
}
]
}

This means that the trade-in is now registered. If there is a need to cancel the trade-in due some error, then request DELETE /api/v2/retail/tradeins/{tradeInId} shall be used sending there trade-in ID as only parameter. If there is a need to get also PDF for the trade-in, then following request needs to be made using the same trade-in Id: GET /api/v2/retail/tradeins/{tradeInId}/pdf

Request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/tradeins/101386/pdf?api-version=2.0

 

As a response You will get the download link for PDF.

6. Temporary Trade-In Registration

We also have the option to register a “temporary” trade-in. The requests and their responses are identical, only you need to add “temp” to the call like this GET /api/v2/retail/tradeins/temp/.
Temporary trade-in has been designed for online trade-in, where customer can originally evaluate the device on his/her own, but before confirming the final value you’d like to re-grade the device at your stores or a central warehouse, before shipping to Foxway.

7. Custom Data Fields & Validation

In order to be able to register a trade-in with proper data as configured in the back-end, then we also need to know which data should/could be sent to Foxway during registration.
For that we first need to intiate a GET /api/v2/retail/configurations/data-fields call.

Example request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/configurations/data-fields?api-version=2.0


Response:

{
"IsCampaignField": false, //if this is set as “true”, the field can be ignored from this call – it is just informative & field configuration will only apply if campaign is also specified in PriceQuote call – we will cover
Campaigns in further section of this document.
"IsRequired": false,
"IsVisible": true,
"Scope": "Corporate, Private",
"Label": "Customer phone", //Field name, already translated, which should be shown to user.
"SortOrder": 4,
"DataFieldId": 25, //FieldId, used for sending back the field value during Trade-In registration
"DataFieldName": "ClientPhone",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
},
{
"IsCampaignField": false,
"IsRequired": true,
"IsVisible": true,
"Scope": "Corporate, Private",
"Label": "Customer email",
"SortOrder": 3,
"DataFieldId": 26,
"DataFieldName": "ClientEmail",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
},
{
"IsCampaignField": false,
"IsRequired": true,
"IsVisible": true,
"Scope": "Corporate",
"Label": "Organization name",
"SortOrder": 0,
"DataFieldId": 70,
"DataFieldName": "Organization name",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
},
{
"IsCampaignField": false,
"IsRequired": true,
"IsVisible": true,
"Scope": "Corporate, Private",
"Label": "First name",
"SortOrder": 1,
"DataFieldId": 71,
"DataFieldName": "First name",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
},
{
"IsCampaignField": false,
"IsRequired": true,
"IsVisible": true,
"Scope": "Corporate, Private",
"Label": "Last name",
"SortOrder": 2,
"DataFieldId": 72,
"DataFieldName": "Last name",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
},
{
"IsCampaignField": true, //as stated earlier, ignore this in this phase. These fields also appear under Campaign details, which are described in more detail under Campaigns section of this document.
"IsRequired": true,
"IsVisible": false,
"Scope": "",
"Label": "NextGenApp",
"SortOrder": 0,
"DataFieldId": 1751,
"DataFieldName": "NextGenApp",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"ListOptions": []
}


From this call it’s important to note down the data fields information; if it’s related to a campaign or not; if it’s expected for customer type IsCorporate=True or False; Data Field Label; if it’s Required or not; what type of field it should be; in case of ListOptions what options should be presented to user.

DataFieldId is the Id that should be used for providing the Data Field Value during Trade-In registration process.

Furthermore we also provide Validation for data fields – to be able to already show user if value of a certain field is acceptable before submitting the trade-in registration call.

For that we need to intiate a GET /api/v2/retail/tradeins/datafields/validate call, where dataFieldId and entered value are already within the request URL.

Example request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/tradeins/datafields/validate?value=5555&dataFieldId=1643&api-version=2.0


Response:

{
"Result": "Invalid",
"ResultType": 1,
"Message": "Data field '1643' value '5555' is not accepted."
}


The response shows if submitted data field value is accepted or not. This would be good practice in terms of UX to already show this out for each field before trade-in registration is attempted.

8. Campaigns

We provide support for campaigns. Available campaigns and their exact content will be configured by Foxway in the backend according to customer requirements, but as there can be many different campaigns and also option for trade-in without a Campaign, then we need to handle campaigns properly also via the API.
To get a list of all campaigns, we need to initiate this call: GET /api/v2/retail/campaigns/
Example request:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/campaigns/list?api-version=2.0


Response:

{
" {
"Data": [
{
"Id": 6300, //CampaignId which should be posted under PriceQuote call (CampaignId).
"Name": "16 SEK", //campaign name to be shown out to customer.
"ActiveFrom": "2021-07-27T00:00:00",
"ActiveTo": "2022-05-31T23:59:59",
"Description": "",
"OverrideVendorTC": false,
"TermsAndConditions": "",
"PromoCode": "16 SEK",
"HasFmipDeduction": true,
"MaxDevicesPerTradeIn": 1, // limitation of number of devices to.
"Status": "Active", //Campaign can only be used if it’s in “Active” status. Campaigns can also be “Expired” (historic campaigns) or “Upcoming” which means that campaign has been scheduled, but is not yet active – date range
given above as “ActiveFrom” and “ActiveTo”.
"Strategy": "Sum"
},
{
"Id": 7540,
"Name": "Förköp extra inbytesvärde Samsung S22",
"ActiveFrom": "2022-02-28T00:00:00",
"ActiveTo": "2022-04-30T23:59:59",
"Description": "",
"OverrideVendorTC": false,
"TermsAndConditions": "",
"PromoCode": "Förköp extra inbytesvärde Samsung S22",
"HasFmipDeduction": true,
"Status": "Active",
"Strategy": "Sum"
}
]
}

The above response shows the campaign basic info. If you’d now want to apply an active campaign, then you must specify the Campaign ID in the PriceQuote call (described under Device Condition & Price Lookup) to already get the device price with Campaign subsidy included in the price + potential campaign bonuses.

To register a trade-in with PriceQuote where campaign is applied, you also need to query campaign details – as there might be cases where a campaign includes specific data fields, which are only relevant when campaign is chosen.

For that we need to intiate a GET /api/v2/retail/campaigns/details/{id} call, where “id” is the Id of the specific campaign.

For example:

https://api--uat.qa.rc.foxway.dev/api/v2/retail/campaigns/details/457?api-version=2.0


Response:

{
"DataFields": [
{
"IsRequired": true,
"IsVisible": true,
"Label": "New Device Model",
"SortOrder": 0,
"DataFieldId": 51,
"DataFieldName": "Baltics Samsung -New device REGULAR",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"IsCampaignField": false,
"ListOptions": []
},
{
"IsRequired": true,
"IsVisible": true,
"Label": "New device IMEI",
"SortOrder": 0,
"DataFieldId": 52,
"DataFieldName": "Baltics Samsung - IMEI code REGULAR",
"DataFieldTemplateType": "Text",
"DataFieldType": "Literal",
"IsCampaignField": false,
"ListOptions": []
}
],
"Offers": [
{
"Amount": 100,
"OfferCode": "",
"AdjustmentType": "Flat",
"Grade": "",
"OfferType": "Deal"
}
],
"Id": 457,
"Name": "+100€ campaign bonus",
"ActiveFrom": "2020-08-19T00:00:00",
"ActiveTo": "2022-04-30T23:59:59",
"Description": "Description of the campaign or info too sales staff goes here...",
"OverrideVendorTC": false,
"TermsAndConditions": "T&C of campaign goes here...<br><br>Legal text.....",
"PromoCode": "+100€ campaign bonus",
"HasFmipDeduction": false,
"Status": "Active",
"Strategy": "Max"
}


The response shows all the details about the specific campaign, but in terms of implementation it is only relevant to note down the info about Data Fields. If you apply a campaign to a PriceQuote, then during trade-in registration process it is expected that the campaign-related data fields are included as well – exactly in the same format as regular custom data fields.

Before registration you can also apply data field validation in the same exact way as described under Custom Data Fields & Validation.

NB! Regarding campaigns please keep in mind that under a single trade-in, only 1 campaign can be used! You can not mix&match Price Quotes without campaign with the ones including a campaign and you can not mix/match Price Quotes with one campaign with a Price Quote that has another campaign applied.

9. Shipping Functions

We have a 3-layer shipping set-up: Package->Shipment->Pickup order.

For creating a package the request POST /api/v2/retail/packages needs to be called. There needs to be all the TradeInDeviceId’s that will be put in to that package. The TradeInDeviceId is returned back during TradeIn Registration process but can also be queried any time via the call GET /api/v2/retail/tradeins/device which will return all traded in devices with details & their current status.

Request for creating a package with items in it:

{
"Name": "Name of the Package",
"Identifier": "123456789",
"PackageDimensionId": 6,
"AgentId": null, //can be used to specify which user is behind the action – described at a later part of this document.
"AgentRef": "Client side reference",
"LocationId": //should be used to specify at which location the package is created – described at a later part of this document.
"LocationRef": "Client side reference",
"TradeInDeviceIds": [
106752
]
}

Response is just package ID:

11759

If packages are made then they need to be added into the shipment using request POST /api/v2/retail/shipments using the created packages IDs.

Request:

{
"Name": "Name of the shipment",
"AgentId": null, //can be used to specify which user is behind the action – described at a later part of this document.
"AgentRef": "Client side reference",
"LocationId": null, //please keep in mind packages from only a single location should be added to one shipment.
"LocationRef": "Client side reference",
"PackagesIds": [
11759
]
}

 

Response is just a shipement number:

7559


Now that you have created the shipment you are able to order a shipping label for the package with the call POST /api/v2/retail/shipments/{shipmentId}/label

Request:

{
Foxway Retail API 21
"LocationId": 0, // our location ID where this shipment has been created at.
"LocationAddress": {
"CountryCode": "string",
"City": "string",
"PostCode": "string",
"Street1": "string",
"Street2": "string",
"PhoneNumber": "string"
}
}


Response is just a download URL for the label which should be attached on the shipment:

{
"DownloadUrl": "string"
}

 

There is also option to delete the packages/shipments or add devices into existing packages and packages into existing shipments using API. You can also cancel the generated shipping label. Further info on these via Swagger.

Our shipping functionality is currently in development and will be updated with next API documentation versions.

10. Users & Locations

We’ve mentioned AgentId and LocationId in several calls. These can be left empty, in which case we tie the actions to the User and Location tied with the API key in use. However in case of PoS integrations & to be able to correctly use some of the other functionality, i.e. shipments, we suggest to provide the correct information.

To get a list of available users, we need to do GET /api/v2/users

For example:

https://api--uat.qa.rc.foxway.dev/api/v2/users?api-version=2.0


Response:

{
"UserId": "string", //this is what needs to be posted under AgentId in other calls.
"UserName": "string",
"FirstName": "string",
Foxway Retail API 22
"LastName": "string",
"PhoneNumber": "string",
"Email": "string",
"CreateDate": "2022-04-01T13:43:37.0575437",
"Locations": [
"string"
],
"LocationIds": [ //this is what needs to be posted under LocationID in other calls.
0
],
"UserExternalReference": "string",
"IsActive": true
}


If needed you can also query a list of locations separately with GET /api/v2/locations.

11. PDF Signing

For in-store PoS integration we offer the possibility to go paperless via adding customer & sales agent signatures to the trade-in confirmation PDF already via API – after trade-in has been registered you could capture signatures via a signature pad & then send them over to our API as form-data with the following calls:

Customer signature: POST /api/v2/retail/tradeins/{tradeinid}/signature/client

Sales agent signature: POST /api/v2/retail/tradeins/{tradeInId}/signature/agent

More detailed info available in Swagger.

12. Error Messages

GetTradeInDataFieldsByTerm:
B040001 - Term must contain at least three characters.
B040002 - Term is required.
N040002 - DataField with '{0}' id does not exist in DB. - Parameters: (Int32 id)

EnabledRetailToolRequiredMessages, GetCheckMendQueryCriteria, ValidateTradeInDataField:
B050001 - Value is required.
N050001 - DataField with '{0}' id does not exist in DB. - Parameters: (Int32 id)
B050002 - DataField Id is required.
N050002 - Model not found.
B050003 - Value is already used.
B050004 - Data field is not active.
B050005 - Data field '{0}' value '{1}' does not match Regex '{2}'. - Parameters: (Int32 dataFieldId, String attemptedValue, String regex)
B050006 - Data Field value is not valid numeric.
B050007 - Data field '{0}' value '{1}' is unknown. - Parameters: (Int32 dataFieldId, String attemptedValue)
B050008 - Data field '{0}' value '{1}' is not accepted. - Parameters: (Int32 dataFieldId, String attemptedValue)
B050009 - Data field '{0}' value can not be more than 500 symbols. - Parameters: (Int32 dataFieldId)
B050010 - Error in external data fields validation: '{0}' - Parameters: (String externalMessage)
B050031 - Retail Tool is disabled

AddItemsApi, CancelPackageApi, CreatePackageApi, GetPackageDetailsApi:
B060001 - Package already linked to shipment
C060001 - Trade-in item already belong to package - Parameters: (Int32 tradeInItemId)
N060001 - Package does not exist
S060001 - Package Not Created
B060004 - Tradein Item should belong to the current vendor - Parameters: (Int32 tradeInItemId)
B060005 - Location does not exist
B060006 - Package with specified Identifier already exist
B060007 - Package name is required
B060008 - Device not found by Id - Parameters: (Int32 tradeInItemId)
B060009 - TradeInDeviceId required
N060009 - Package not found by id
B060010 - Package should be in Open or Packed status
B060011 - Trade-in item already in package
B060012 - Trade-in item does not exist - Parameters: (Int32 tradeInItemId)
B060013 - Package Id required
B060014 - Package status should be opened or packed
B060015 - Can not add Canceled/Withheld devices to packages - Parameters: (Int32 tradeInItemId)
B060016 - User does not exist - Parameters: (String agentId)
B060017 - Found duplicate TradeInDeviceId - Parameters: (Int32 tradeInDeviceIds)
B060018 - Device with wrong step can not be added - Parameters: (Int32 tradeInItemId)
B060019 - Device with none registered status can not be added - Parameters: (Int32 tradeInItemId)
B060020 - All devices must be with the same destination
B060021 - All devices must be with the same shipping flow type
B060022 - Package has wrong step
B060023 - Device has mismatch flow type compared to the package - Parameters: (Int32 tradeInItemId)
B060024 - Device has mismatch destination compared to the package - Parameters: (Int32 tradeInItemId)
B060025 - Please enter a valid PackageDimensionId
B060026 - For Trade In Device with id '{0}' shipping is not allowed now - Parameters: (Int32 tradeInDeviceId)

PriceQuoteRequest:
N070002 - Question with '{0}' id not found in this grading form version. - Parameters: (Int32 id)
N070003 - Answer option with '{0}' id not found in this grading form version. - Parameters: (Int32 id)
N070004 - Extra with '{0}' id not found in this grading form version. - Parameters: (Int32 id)
B070005 - User with '{0}' id does not exist. - Parameters: (String id)
N070005 - Campaign was not found by id '{0}' - Parameters: (Int32 campaignId)
B070006 - Item identifier must be at least 5 characters.
B070007 - Please provide Grading Form Answers
B070008 - Active grading form version for item variant '{0}' not found. - Parameters: (Int32 id)
B070010 - Can not parse grading form
B070011 - Campaign with id '{0}' has not started yet. - Parameters: (Int32 campaignId)
B070012 - Campaign with id '{0}' has already expired. - Parameters: (Int32 campaignId)

AddPackagesToExistingShipmentApi, CancelShipmentApi, CreateShipmentApi, GetShipmentDetailsApi, UpdateShipmentApi:
B080001 - Package already added in shipment - Parameters: (Int32 packageId)
N080001 - Shipment not found
B080002 - Shipment name required
N080002 - Package not found - Parameters: (Int32 packageId)
B080003 - Packages required to create shipment
N080003 - Location not found
B080004 - Name required
N080004 - User not found
B080005 - Only shipments in Ready status can be cancelled
B080006 - Shipment id required
B080007 - Found duplicate PackageId - Parameters: (Int32 packageId)
B080008 - Package has wrong step - Parameters: (Int32 packageId)
B080009 - All packages must be with the same destination
B080010 - All packages must be with the same shipping flow type
B080011 - Package has mismatch flow type compared to the shipment - Parameters: (Int32 packageId)
B080012 - Package has mismatch destination compared to the shipment - Parameters: (Int32 packageId)
B080013 - Empty package can not be shipped - Parameters: (Int32 packageId)
B080014 - Unable to add packages to shipment with id '{0}' that has Label - Parameters: (Int32 shipmentId)

CancelTradeInDevice, CancelTradeInRequest, ConfirmTempTradeInRequest, GetCo2Emission, GetPriceQuote, GetTempTradeInDetails, GetTradeInDetails, GetTradeInPdf, RegisterTradeInRequest, UpdateTradeInRequest:
B090001 - Trade-in should be in Registered status.
N090001 - Trade-In does not exist.
N090002 - User not found.
N090003 - Price quote with '{0}' id not found. - Parameters: (Guid id)
B090004 - Unfinished Trade-in item with '{0}' identifier already exist. - Parameters: (String identifier)
B090005 - Item with '{0}' identifier already in stock. - Parameters: (String identifier)
N090005 - Trade-in id required.
B090006 - TradeInDevices can't be empty.
N090006 - Trade-In not found.
B090007 - User with '{0}' id does not exist. - Parameters: (String id)
N090007 - Price quote id required.
B090008 - Location with '{0}' id does not exist. - Parameters: (Int32 id)
B090009 - Perform due diligence check for '{0}' device. - Parameters: (String identifier)
B090010 - Due diligence status Failed is not allowed for '{0}' device. - Parameters: (String identifier)
B090011 - FMIP status Lost is not allowed for '{0}' device. - Parameters: (String identifier)
B090012 - Due diligence status Caution is not allowed for '{0}' device. - Parameters: (String identifier)
N090012 - Item identifier required.
B090013 - Due diligence status Error is not allowed for '{0}' device. - Parameters: (String identifier)
N090013 - Temporary trade-in was not found by id '{0}'. - Parameters: (Int32 tempTradeInId)
B090014 - Duplicate data field value with '{0}' id. - Parameters: (Int32 id)
B090015 - Devices already packed.
B090016 - Required data field with '{0}' name is empty. - Parameters: (String name)
B090017 - Data field with '{0}' id not found. - Parameters: (Int32 id)
B090018 - Not valid data field '{0}' - Parameters: (String fieldValue)
B090019 - '{1}' can't be used as value for data field '{0}' because it has been already used. - Parameters: (Int32 dataFieldId, String attemptedValue)
B090021 - Max limit of devices in trade-in has been exceeded.
B090022 - Can not register trade-in with device '{0}' because it is in process of registering initialized by another API call. Try again in {1} seconds. - Parameters: (String identifier, UInt32 tryAgainAfterSeconds)
B090023 - Identifier should be unique, but Identifier '{0}' was met {1} times in request. - Parameters: (String identifier, UInt32 nonUniqueCount)
B090024 - Device with '{0}' price quote already registered. - Parameters: (Guid id)
B090025 - Data field '{0}' value '{1}' does not match Regex '{2}'. - Parameters: (Int32 dataFieldId, String attemptedValue, String regex)
B090026 - '{1}' can't be used as value for '{0}' data field '{2}'. - Parameters: (Int32 dataFieldId, String attemptedValue, String requiredType)
B090027 - Data field '{0}' value '{1}' is unknown. - Parameters: (Int32 dataFieldId, String attemptedValue)
B090028 - Data field '{0}' value '{1}' is not accepted. - Parameters: (Int32 dataFieldId, String attemptedValue)
B090029 - Data field '{0}' value can not be more than 500 symbols. - Parameters: (Int32 dataFieldId)
B090030 - The PriceQuoteId '{0}' has already been used for another trade-in. Please make a new PriceQuote. - Parameters: (Guid valuationId)
B090031 - Required data field '{0}' is empty. - Parameters: (Int32 dataFieldId)
B090032 - All valuations must have the same Campaign id.
B090033 - Campaign with id '{0}' has not started yet. - Parameters: (Int32 campaignId)
B090034 - Campaign with id '{0}' has already expired. - Parameters: (Int32 campaignId)
B090035 - Campaign offer currency does not match vendor currency.
B090036 - Temporary trade-in with id '{0}' is expired. Please register it again. - Parameters: (Int32 tempTradeInId)
B090037 - Required devices are null.
B090038 - Can not register without valuations.
B090039 - TradeIn already registered.
B090040 - Temporary trade-in with id '{0}' was cancelled. Please register it again. - Parameters: (Int32 tempTradeInId)
B090041 - Temporary trade-in with id '{0}' was already confirmed. - Parameters: (Int32 tempTradeInId)
N090041 - Partner product '{0}' not found in fpc transaction. - Parameters: (Int32 partnerProductId)
B090042 - Maximum trade-in amount exceeded. Please remove some items from the cart.
N090042 - Fpc transaction should be completed or succeed.
B090043 - Limit of using the same {0} is reached - Parameters: (Int32 dataFieldId)
N090043 - Fpc device with this identifier already exists
B090044 - Trade In Reference Number must be at least 3 characters
B090045 - Trade In Reference Number should not contain spaces
B090046 - Device was already registered. - Parameters: (String deviceIdentifier)
B090047 - Please validate DataField with id '{0}' before registering Trade-in. - Parameters: (Int32 dataFieldId)
B090048 - Any data for updating hasn't been provided.
B090049 - Please validate DataField with id '{0}' before updating Trade-in. - Parameters: (Int32 dataFieldId)
B090056 - Client signature storage file should belong to current user.
B090057 - Agent signature storage file should belong to current user.
B090058 - The device with '{0}' is object of FPC. - Parameters: (String identifier)
B090059 - Trade In Reference Number should not contain spaces
B090060 - Trade In Reference Number must be not more then 100 characters
B090062 - Trade In Reference Number already exists - Parameters: (String tradeInRef)
B090063 - Trade In Reference Number must be at least 3 characters
B090064 - Trade-in can not be registered with specified price quote {0}, because price quote created out of scope of Campaign {1}. - Parameters: (Guid priceQuoteId, Int32 campaignId)
B090065 - Device identifier of Trade-in device #{0} should not be null - Parameters: (Int32 tradeInDeviceNumber)
B090101 - Register trade in configuration not found.

GetDeviceCheckResult, GetDueDiligenceReport:
B160001 - ItemIdentifier required
N160001 - Report not found
B160003 - ReportId is required

GetGradingFormBlankByItemVariantId:
N170008 - Active grading form version for item variant '{0}' not found. - Parameters: (Int32 id)

GetCampaignDetailsApi:
N180001 - Campaign not found

UpdateTradeInItemStatusWithheldAPI:
B200001 - Trade-In Item Not found (or attached to another vendor).
B200002 - Trade-In Item already processed and can not be withheld.
B200004 - Trade In Device with has wrong flow type
B200005 - Trade in Device has a wrong step or status
B200006 - Trade In Device with has wrong status

GenerateTradeInPublicUrl:
N210001 - Trade-in with id '{0}' was not found - Parameters: (Int32 tradeInId)
B210002 - Trade-in '{0}' was canceled - Parameters: (Int32 tradeInId)
B210003 - The source string is required.

GenerateTempTradeInPublicUrl:
N220001 - Temp trade-in with id '{0}' was not found - Parameters: (Int32 tempTradeInId)
B220002 - Temp Trade-in '{0}' is expired. - Parameters: (Int32 tempTradeInId)
B220003 - Temp Trade-in '{0}' is canceled. - Parameters: (Int32 tempTradeInId)
B220004 - Temp Trade-in '{0}' is confirmed. - Parameters: (Int32 tempTradeInId)

CreateShipmentLabelApi:
N230001 - Shipment with id '{0}' not found - Parameters: (Int32 shipmentId)
B230002 - Shipment with id '{0}' does not belong to current vendor - Parameters: (Int32 shipmentId)
B230003 - Shipment with id '{0}' from another location - Parameters: (Int32 shipmentId)
B230004 - Shipment with id '{0}' was cancelled - Parameters: (Int32 shipmentId)
B230006 - The ExternalId for Shipment with id '{0}' was already set - Parameters: (Int32 shipmentId)
B230008 - Shipment with id '{0}' should be in status 'Ready' - Parameters: (Int32 shipmentId)
B230009 - Shipment with id '{0}' should have only one package inside - Parameters: (Int32 shipmentId)
B230010 - Internal package should have special dimensions
B230011 - NShift is disabled for current vendor
N230012 - Location with id '{0}' is not associated with the current vendor - Parameters: (Int32 locationId)
B230013 - CountryCode required
B230014 - City required
B230015 - PostCode required
B230016 - Street1 required

CancelShipmentLabelApi:
N240001 - Shipment with id '{0}' not found - Parameters: (Int32 shipmentId)
B240002 - Shipment with id '{0}' does not belong to current vendor - Parameters: (Int32 shipmentId)
B240003 - Shipment with id '{0}' was cancelled - Parameters: (Int32 shipmentId)
B240004 - Shipment with id '{0}' should be in status 'Ready' - Parameters: (Int32 shipmentId)
B240006 - The ExternalId for Shipment with id '{0}' was not set - Parameters: (Int32 shipmentId)
B240007 - NShift is disabled for current vendor

DownloadShipmentLabelApi:
N250001 - Shipment with id '{0}' not found - Parameters: (Int32 shipmentId)
B250002 - Shipment with id '{0}' does not belong to current vendor - Parameters: (Int32 shipmentId)
B250003 - Shipment with id '{0}' from another location - Parameters: (Int32 shipmentId)
B250004 - Shipment with id '{0}' was cancelled - Parameters: (Int32 shipmentId)
N250006 - The Label for Shipment with id '{0}' not found - Parameters: (Int32 shipmentId)

GetShippingStatusHistoryApi:
N260001 - Shipment was not found by id '{0}' - Parameters: (Int32 shipmentId)

CreateShipmentByTradeInApi:
N270001 - Trade-in does not exist - Parameters: (Int32 tradeInId)
B270002 - Package dimension is not specified in vendor shipping configuration in Luna.

UpdateCustomerStatusApi:
B280001 - Trade-In Device Id is required
N280002 - Trade-In Device with id: '{0}' was not found in DB. - Parameters: (Int32 tradeDeviceId)
B280003 - New customer status is not defined.
B280004 - New customer status '{0}' is invalid. - Parameters: (CustomerStatus newCustomerStatus)
B280005 - Customer status is already set to '{0}'. - Parameters: (CustomerStatus currentCustomerStatus)

GetCustomerStatusApi:
N290001 - Trade-In Device with id: '{0}' was not found. - Parameters: (Int32 tradeInDeviceId)
B290002 - Trade-In Device Id required.