About Ship to Store (STS)
Using local retailers as Ship-to-Store pickup sites, service points, or in-store returns is becoming a key need for every brand. Locally makes this easy by leveraging your existing, managed dealer network. We enable each dealer who is participating in Buy it Locally to accept shipments for prepaid customers. The process is seamless, shipping fees are minimal as compared to residential shipments, and is supportive of your wholesale channel.
Configuring Your Ship to Store Profile
If you don’t already have a Locally brand account, please contact your Locally representative about getting your company account set up. Once your account is set up you can manage basic settings for your Ship to Store account at https://www.locally.com/station/panel/shipping_configurations.
The following fields are editable in this configuration panel:
"Available to Sell" Feed
You will need to provide and host a single CSV document indicating which of your company’s UPCs are available to sell. This file has a very simple format, containing only 5 fields: UPC, Quantity, Percent Revenue Retained, Shipping Cost and Country (in that order). Locally will scan this feed at least once per hour. The UPC and Quantity field are self-explanatory. Percent Retained
is the percent of total revenue from the customer that your company will retain when a purchase of this UPC is made. Shipping Cost
is the amount that the customer will be charged for having the item shipped to the store. See the “Billing Considerations” section below for more info on these fields.
Note: Locally recommends not adding any shipping charges to be passed on to the customer. Only large/heavy items that would otherwise be more expensive to ship directly to a customer’s residence should have an additional shipping fee. By default the shipping fee for the customer is $0.
Feed format sample:
upc | qty | percent revenue retained | shipping cost | country |
---|---|---|---|---|
727602259178 | 1 | 70 | 0 | US |
605284574539 | 2 | 70 | 25.50 | US |
736745040314 | 100 | 58 | 0 | US |
Alternatively, you can submit revenue splits by defining the dollar amount the retailer should receive on the order, which we call “monetary splits.” To use this method, replace the percent revenue retained
column with a column titled split_flat
and include the amount of revenue the retailer should retain on an order for that product. The brand is credited for the leftover amount.
Alternate feed format sample:
upc | qty | split_flat | shipping cost | country |
---|---|---|---|---|
727602259178 | 1 | 250 | 0 | US |
605284574539 | 2 | 500 | 25.50 | US |
736745040314 | 100 | 100 | 0 | U |
Availability of your products across all of your retailers in the Locally ecosystem may take a bit longer than one hour to propagate. But changes will be reflected at all retailer locations within no more than 3 hours of any detected change.
It is your responsibility to keep this feed updated. If the feed is not available for Locally to scan, we will temporarily disable Ship-to-Store functionality for your brand. We will continue to scan for the feed and will re-enable functionality when the feed has been restored.
Webhook URL for Order Updates
You will need to provide Locally with a webhook URL endpoint. This URL can be changed at any time via the STS settings configuration panel. Locally will send a payload containing the cart
object (see API documentation below) to this endpoint whenever a new order is created or has been updated. It will be your responsibility to consume this payload and act accordingly in order to fulfill the request in a timely manner. See the “Buy it Locally: Real-time Carts API” section below for more information on this topic.
Default Revenue Percent Retained
As mentioned earlier, your “available to sell” feed will inform Locally about the percentage of customer revenue that will be retained by your company. However, you can set a default percentage in the STS configuration panel to handle cases where either a) there is no percent defined for a UPC in the “available to sell” feed. Or b) no percentages have been defined in the feed at all.
Real-time Carts API
The Real-time Carts API is your way to request up-to-the-minute information on all orders you have access to, and how to update an existing order’s status that resides within the Locally system.
API Key
You will need an API Key to access your CartAPI endpoint. See Generate An API Key for more info.
Once you have your API key, you will be able to access our /carts
API endpoints.
GET /carts/{page}
endpoint
/carts/{page}
endpointThe way a request is built is by adding “Locally-API-Token” to the header of your request, and the value of that will be the API key you generated on our platform.
To access the list of all the carts, access:
GET https://www.locally.com/api/v2/carts
You will need to pass along your API Key in the header of the request. For example, in cURL:
curl -X GET <https://www.locally.com/api/v2/carts> -H 'Locally-API-Token: ea432b8a353ef459be46f147b33ae37f0cae2337'
This will return the list of all carts your account has access to, ordered by most recent first, and broken up via 50 per page. First page can be accessed via just /api/v2/carts
or /api/v2/carts/0
Sample, abbreviated payload:
{
"status": true,
"properties": {
"date_time": "2020-03-29 03:31:25",
"current_page": 0,
"total_pages": 6
},
"carts": [{
"hash": "O3V36",
"type": "SHIP",
"source": "From Locally.com",
"n_items": 1,
"ordered_at": "2020-03-28 11:34:21",
"updated_at": "2020-03-28 12:41:04",
"delivery_status": "Delivery fee has been reconciled",
"pickup_window": "2020-03-28 18:00:00",
"items": [{
"currency": "USD",
"total": 110.85,
"status": "CONFIRMED",
"sts_status": "",
"qty": 1,
"product_id": 156732,
"order_resolution": "SUCCESS",
"upc": "190340561721",
"alias": [
"D1447020122-00003"
]
}]
}, {
"hash": "4RT36",
"type": "ORDER",
"source": "From Locally.com",
"n_items": 2,
"ordered_at": "2020-03-27 15:34:21",
"updated_at": "2020-03-27 17:41:04",
"delivery_status": "Delivery fee has been reconciled",
"pickup_window": "2020-03-28 18:00:00",
"items": [{
"currency": "USD",
"total": 54.85,
"status": "CONFIRMED",
"sts_status": "CONFIRMED",
"qty": 1,
"product_id": 356732,
"upc": "190340561721",
"alias": [],
"order_resolution": "SUCCESS"
}, {
"currency": "USD",
"total": 165.36,
"status": "CONFIRMED",
"sts_status": "CONFIRMED",
"qty": 1,
"product_id": 254385,
"upc": "190340561726",
"alias": [],
"order_resolution": "SUCCESS"
}]
}]
}
Definition of the response payload:
Field | Description |
---|---|
status | Boolean. true if request was a success, false if not. |
message | Only sent if status was false, gives the reason for the false status. |
properties.date_time | The datetime of this request. |
properties.current_page | The current page you are on. Pagination starts at page 0 (zero). |
properties.total_pages | Total number of pages. If total pages is 5 , then pages 0, 1, 2, 3, 4 exist. |
carts | Array of carts. |
carts[].hash | The unique hash for the order. For example E3GKY8 . |
carts[].type | HOLD for a reservation for in-store payment.ORDER for a payment for in-store pickup.SHIP for a Ship-to-Store order (This is a special case for select brands only. This transaction type is NOT available for POS vendor or retailer integrations). |
carts[].n_items | Number of items in the order. |
carts[].source | Domain where the order originated from. “From Locally.com”, “From brand-site.com”, etc. |
carts[].ordered_at | The datetime of when the order was placed. |
carts[].updated_at | The datetime of when the order has changed last. |
carts[].delivery_status | If the cart is a Same-Day Delivery, this is its current status. PushCart does not support Same-Day Delivery at this time, so this will be blank. |
carts[].pickup_window | The datetime string of the selected pickup window slot time or empty string. |
carts[].items | Breakdown of items in carts statuses and basic information. Please note that there can be more than one item per cart. |
carts[].items[].currency | The currency the order is in. Adheres to ISO 4217 |
carts[].items[].total | The total amount for the item. Includes tax if this is a "Buy Online, Pickup in Store (BOPIS)" order. |
carts[].items[].status | The status of the item:Ordered : The BOPIS or ROPIS request has been placed and has not been resolved by the store.Confirmed : The store has confirmed that the item is in stock and is being held for the shopper.Canceled : The store has rejected the BOPIS or ROPIS request.Charge_Failed : There was an issue charging the shopper’s credit card on a BOPIS order and payment was not captured. This is rare but should be accounted for. The store should treat these like reservation requests and collect payment when the shopper arrives. |
carts[].items[].sts_status | This is for Ship-to-Store orders only. If the store is not participating in Ship-to-Store, this will always read “N/A”. |
carts[].items[].qty | Quantity of this item. |
carts[].items[].product_id | The Locally Product ID of the item. |
carts[].items[].order_resolution | The order resolution, SUCCESS , DECLINED , etc. This gives more detail as to why/how the item status is what it is. The full list of order resolution statuses can be found later in this document under Other API Functionality: Order Resolution. |
carts[].items[].upc | The UPC/EAN of the item. |
carts[].items[].alias | Array of UPC/EAN aliases or empty array in case of no aliases |
Reference Doc: /carts/{page}
GET /cart/{cart_hash}
endpoint
/cart/{cart_hash}
endpointThis will give you all the data for this specific cart. Your company must have access to this cart in order to retrieve its data.
To access this single cart’s information, use this endpoint:
GET https://www.locally.com/api/v2/cart/{cart_hash}
You will need to pass along your API Key in the header of the request. For example, in cURL:
curl -X GET <https://www.locally.com/api/v2/cart/PDQE89> -H 'Locally-API-Token: ea432b8a353ef459be46f147b33ae37f0cae2337'
Sample, abbreviated payload:
{
"status": true,
"properties": {
"date_time": "2021-02-02 12:48:03",
"type": "ORDER",
"hash": "PDQE89",
"pickup_window": "2021-02-06 18:00:00",
"updated_at": "2021-01-22 13:11:57",
"delivery_status": "",
"invoice": "https://www.locally.com/order/PDQE89/SJQWNEFK3966?print=1"
},
"customer": {
"first_name": "John",
"last_name": "smith",
"email": "John.smith0@gmail.com",
"phone": "+18054031225"
},
"store": {
"name": "Hypercat Cycleworks",
"address": "555 University Ave",
"phone": "222-333-4444",
"city": "Ventura",
"state": "CA",
"zip": "93003",
"store_id": 128943,
"vendor_id": 0
},
"items": [
{
"status": "Confirmed",
"sts_status": "Processing",
"product_id": 531197,
"qty": 1,
"brand": "Tron Bicycles",
"upc": "811950030719",
"alias": [],
"name": "The E-bike",
"attrib_1": "Medium",
"attrib_2": "",
"currency": "USD",
"msrp": "2,099.99",
"tax": "162.75",
"tax_remitted": true,
"commission": "73.50",
"total": "2,262.74",
"disbursement": "1,262.74",
"order_resolution": "",
"resolution_notes": "",
"messages": [
{
"sender": "locally",
"description": "Please accept or decline this order. If you do not reply within 2 hours, we'll auto accept it.",
"created_at": "2021-01-17 15:04:06"
},
{
"sender": "locally",
"description": "You approved this item for the Ship-to-Store order.",
"created_at": "2021-01-17 15:11:53"
},
{
"sender": "locally",
"description": "The E-bike has shipped.",
"created_at": "2021-01-18 07:03:20"
},
{
"sender": "customer",
"description": "Hi, what’s the lead time on delivery?\r\n\r\nJohn",
"created_at": "2021-01-18 12:23:14"
},
{
"sender": "locally",
"description": "Tron Bicycles is processing this item, you will be notified when it ships.",
"created_at": "2021-01-18 12:32:42"
},
{
"sender": "retailer",
"description": "The bike is in process and will be shipped in the next day or two. Average ship time is 4 days. Puts delivery, built and ready for pick up right about 1 week. If it is ready sooner you will be notified that the bike is ready to pick up. These are fun bikes and we have had great success with them. Good choice",
"created_at": "2021-01-18 12:37:23"
},
{
"sender": "customer",
"description": "Great, thanks",
"created_at": "2021-01-18 12:38:30"
},
{
"sender": "retailer",
"description": "John - your bike is in and will be ready for pick-up today Friday between 1pm and 6pm or during the weekend 12-4. Let me know if you have any questions. \r\n\r\nPhil",
"created_at": "2021-01-22 13:07:48"
},
{
"sender": "customer",
"description": "Great, I’ll come down tomorrow. See you then.",
"created_at": "2021-01-22 13:11:56"
}
]
}
]
}
Definition of the response payload:
Field | Description |
---|---|
status | Boolean. true if request was a success, false if not. |
message | Only set if status was false, gives the reason for the false status. |
properties.date_time | The datetime of this request. |
properties.type | HOLD for a reservation for in-store payment.ORDER for a payment for in-store pickup.SHIP for a Ship-to-Store order (This is a special case for select brands only, opt-in by the retailer is required). |
properties.hash | The hash for the order. Ex. QE3QL6 |
properties.pickup_window | The datetime string of the selected pickup window slot time or empty string. |
properties.ordered_at | The datetime of when the order was placed. |
properties.updated_at | The datetime of when the order has changed last. |
properties.delivery_status | If the cart is a Same-Day Delivery, this is its current status. PushCart does not support Same-Day Delivery at this time, so this will be blank. |
properties.invoice | URL to a minimal PDF of the invoice. |
customer | Customer information |
customer.first_name | The customer’s first name |
customer.last_name | The customer’s last name |
customer.address_1 | If this is a BOPIS order or if the customer has an address on file, their billing street address. Otherwise blank. |
customer.address_2 | If this is a BOPIS order or if the customer has an address on file, their secondary billing address. Otherwise blank. |
customer.city | If this is a BOPIS order or if the customer has an address on file, their billing city. Otherwise blank. |
customer.state | If this is a BOPIS order or if the customer has an address on file, their billing state. Otherwise blank. |
customer.zip | If this is a BOPIS order or if the customer has an address on file, their billing postal code. Otherwise blank. |
customer.country | If this is a BOPIS order or if the customer has an address on file, their billing country. Otherwise blank. |
customer.email | The customers email address |
customer.phone | The customers phone number |
customer.marketing_active_consent | Whether a shopper has opted in to receive marketing emails from the store |
store | Store information |
store.name | The store’s name |
store.phone | The store’s phone number |
store.address | The store’s street address |
store.address_2 | If available, the store’s address 2 information |
store.city | The store’s city |
store.state | The store’s state |
store.zip | The store’s postal code |
store.country | The store’s country |
store.store_id | The unique Locally Store ID |
store.vendor_id | The brand's unique ID for the store, if applicable |
items | Array of items in the cart |
items[].status | The status of the item:Ordered : The BOPIS or ROPIS request has been placed and has not been resolved by the store.Confirmed : The store has confirmed that the item is in stock and is being held for the shopper.Canceled : The store has rejected the BOPIS or ROPIS request.Charge_Failed : There was an issue charging the shopper’s credit card on a BOPIS order and payment was not captured. This is rare but should be accounted for. The store should treat these like reservation requests and collect payment when the shopper arrives.sts_status : This is for Ship-to-Store orders only. If the store is not participating in Ship-to-Store, this will always read false. |
items[].product_id | The Locally product ID of the item |
items[].qty | Quantity of this item in the cart |
items[].brand | The brand / manufacturer name of the product |
items[].upc | The UPC/EAN of the item |
items[].alias | Array of UPC/EAN aliases or empty array in case of no aliases |
items[].name | The product name |
items[].attrib_1 | The product’s first attribute: color, size, etc. |
items[].attrib_2 | The product's second attribute: color, size, etc. |
items[].currency | The currency the order is in |
items[].msrp | The MSRP for the item |
items[].product_price | The item price for which the shopper paid or reserved the item. This can vary from MSRP if the store uses in-store pricing |
items[].product_price_less_vat | If the price includes VAT, the product price less VAT. Please note that this is separate from US and Canadian taxes and applies to VAT countries only. |
items[].subtotal | The subtotal of the item. |
items[].tax | The calculated tax for the item if this is a BOPIS order. |
items[].tax_rate | The tax rate for the item if this is a BOPIS order. For ex., a 6.25% tax rate would appear as 6.25. |
items[].tax_is_vat | Boolean. Whether the tax amount is VAT or not. |
items[].tax_remitted | Whether or not we remitted sales tax on behalf of the store. Locally remits sales tax on behalf of stores in the US and Canada for BOPIS orders only. |
items[].commission | The commission Locally is owed for the item. |
items[].stripe_fee | If this is a BOPIS order, the fee that Stripe charged for the transaction. |
items[].total | The total amount for the item. If this is a BOPIS order, this includes tax. |
items[].disbursement_brand | For Ship-to-store orders only, the revenue split amount payable to the brand on this order (will display as $0 until sts_status = ‘shipped’ ) |
items[].disbursement_retail | For Ship-to-store orders only, the revenue split amount payable to the retailer on this order (will display as $0 until sts_status = ‘picked up’ ) |
items[].split_brand | For Ship-to-store orders only, the brand’s revenue split amount on the order |
items[].split_retailer | For Ship-to-store orders only, the retailer’s revenue split amount on the order |
items[].order_resolution | The order resolution, SUCCESS , DECLINED , etc. This gives more detail as to why/how the item status is what it is. The full list of order resolution statuses can be found later in this document under Other API Functionality: Order Resolution. |
items[].resolution_notes | Notes regarding the order resolution if the store has included them |
items[].messages | Array of messages for the item, if any exist |
items[].messages[].sender | Whether the message was sent by the retailer or the customer |
items[].messages[].description | The contents of the message |
items[].messages[].created_at | When the message was sent |
POST /cart/{cart_hash}/{upc}
endpoint
/cart/{cart_hash}/{upc}
endpointUse this endpoint to make updates to existing orders. See “Lifecycle of a Ship to Store Transaction” for details on the timing of these requests.
You will need to pass along your API Key in the header of the request. For example, in cURL:
curl -X POST <https://www.locally.com/api/v2/cart/O3V36/190340561726> -H 'Locally-API-Token: ea432b8a353ef459be46f147b33ae37f0cae2337' -d '{"sts_status":"processing"}'
Sample payload:
{
"sts_status":"processing",
"reason":"only needed for rejected status"
}
Sample response:
{
"status": true,
}
Managing Store Relationships
Activating your dealers for Ship to Store fulfillment
Any of your retailers can participate in Ship to Store transactions. However, each retailer must have a registered Locally account with “Buy it Locally” enabled. Each retailer must also approve your request for STS participation.
To get started with retailer activation, navigate to https://www.locally.com/station/panel/brand_store_relationships, select each store that you want to make a request to, and click to submit the request.
At the moment, only one request to a store can be made at a time. However, the Locally support team can assist with bulk request submission. Please inquire about this as needed.
Lifecycle of a Ship to Store Transaction
The products in your “available to sell” feed combined along with your activated retailer list will determine which stores display the “Available for Ship To Store” label on product display pages and other product locator technology throughout the Locally platform.

What follows is a description of the workflow starting with the initial customer engagement in the “Buy it Locally” system through to fulfillment (pickup) of the product from the local retailer:
API Integration; Payment, Communications and Order Handoff
This section requires the use of webhooks. Read more about Locally's webhook configuration here.
PENDING BRAND:
The order is placed by the shopper and goes to the brand for confirmation or rejection. The payload to your webhook will contain the UPC, Order #, status, and order URL, as follows:
{
"upc":"811950030719",
"order_id":"A1234",
"sts_status":"confirm-store",
"order_url":"https://www.locally.com/api/v2/cart/A1234
}
The order is now pending your approval.
You must POST to Locally’s cart
API with the status confirm-brand
in order to approve the customer order. For example:
{
"sts_status":"confirm-brand"
}
To reject the order, you must submit the status reject-brand
and supply a reason. For example:
{
"sts_status":"reject-brand",
"reason":"Reason for rejection goes here."
}
If you reject the customer’s request, we will notify the customer and the store about the rejection. You can still communicate with the customer about this order through the Buy it Locally messaging window after an order has been rejected.
If you approve the request, the customer’s credit card will be charged immediately for the full amount. Important note: Locally.com will be the company indicated on the credit card statement.
CONFIRMED:
The shopper is notified that you’ve confirmed the order and their credit card is charged. Once you have started processing it, you must POST to Locally’s cart
API with the status processing
. For example:
{
"sts_status":"processing"
}
Additionally, you can skip the “brand confirmed” step and move a new order directly to “processing.”
PROCESSING:
Customer is now notified that their order is being processed. The customer and store see a system message indicating that the order is in processing.
When the order has shipped, you must POST to Locally’s cart
API with the status shipped
. You can optionally provide a message that will be seen by the customer and retailer. For example:
{
"sts_status":"shipped",
"message":"New Item is on the way to the store. Tracking Number: 9400100000000000000000; Carrier: USPS"
}
The customer and the store will be notified about this in their Buy it Locally messaging window (and via email/SMS).
SHIPPED:
Customer is now notified that their order has shipped to the store. At this point, the brand’s revenue split for this order becomes payable and is applied to your statement.
READY FOR PICKUP:
When the item arrives at the store, it’s the store’s job to alert the customer when the item is ready by changing the order status to “Ready for Pickup.” This is completed via the Order Screen UI all retailers have access to.
PICKED UP:
Once the item is picked up by the customer, the store must change the order status to the final “Picked Up” status. The retailer’s revenue split becomes payable to them at this point.
These status changes will be indicated in the /carts
and /cart
API endpoint.
Management via UI; Payment, Communications and Order Handoff
PENDING BRAND:
Once a new order is received, you must go to the order summary page, and update the Ship-to-Store Status.
To confirm or reject the order, click the button to see a drawer pop out with the options.

If you reject the customer’s request, we will notify the customer and the store about the rejection. You can still communicate with the customer about this order through the Buy it Locally messaging window after an order has been rejected.
If you approve the request, the customer’s credit card will be charged immediately for the full amount. Important note: Locally.com will be the company indicated on the credit card statement.
APPROVED:
Once you have started processing the order, you must go to the order summary page, and update the Ship-to-Store Status to “Processing”.
This will end your ability to communicate directly with the customer.
PROCESSING:
Customer is now notified that their order is being processed. The customer and store see a system message indicating that the order is in processing and can message each other about the order.
When the order has been shipped you must go to the order summary page, and update the Ship-to-Store Status to “Shipped”.
The customer and the store will be notified about this in their Buy it Locally messaging window (and via email/SMS).
READY FOR PICKUP:
When the item arrives at the store, it’s the store’s job to alert the customer when the item is ready by changing the order status to “Ready for Pickup.” This is completed via the Order Screen UI all retailers have access to.
PICKED UP:
Once the item is picked up by the customer, the store must change the order status to the final “Picked Up” status. The retailer’s revenue split becomes payable to them at this point.
Billing Considerations
As mentioned earlier, Locally will be charging the customer directly for Ship-to-Store transactions. This is counter to Locally’s usual practice for “Buy Online, Pickup in Store” where the customer’s funds pass directly to the retailer’s merchant account. As is such, Locally will be responsible for paying out the funds to both your company and the retailer. Payouts will be sent on a monthly basis with a physical check to both parties. Please inquire about alternative forms of payment.
At any time you can check the status of the current month’s payouts at https://www.locally.com/station/panel/billing_invoices. Locally audits each statement between the 1st and the 7th of each month and then issues payouts between the 8th and the 12th.
To better understand how accounting works for brands, please refer to the following table which illustrates the purchase of a $1000 item for Ship-to-Store fulfillment:
Handling Returns and Chargebacks
Once an order is confirmed by the brand, the brand can refund the order up until the order is shipped. On orders refunded prior to shipping, the order simply ends there; no revenue is paid out to either the brand or the retailer.
Once the order has shipped, the product becomes the retailer’s. If a shopper requests a refund from the retailer and the retailer issues a refund, the retailer is expected to take the product into their inventory. On these orders, the brand retains their revenue split, and the retailer is charged for the cost of the product by Locally.
In the event that a retailer’s balance with Locally is negative (for example: they have a refunded order whose amount exceeds their owed payouts from other orders), Locally will charge their credit card for the difference.
Frequently Asked Questions
When does an order become payable to the brand and retailer?
The order becomes payable to the brand once the product is marked as ‘shipped,’ and to the retailer once the product is marked as ‘picked up.’
After a customer makes an order, can we change which store we send the product to and notify the customer?
No. The item must be sent to the store that the customer chose initially.
Can the customer change which store they want us to send the item(s) to after they have submitted an order?
Not at this time.
Can we cancel an order and arrange a deal directly with the customer to ship the item(s) to their residence?
No, this is a violation of our terms of service.