This guide is for creating a checkout experience where items will be added to a Locally cart individually
Creating a new cart and adding items for BOPIS/ROPIS
To begin, create a new cart and add an item to it for either BOPIS (Buy Online, Pick Up In Store) or ROPIS (Reserve Online, Pick Up In Store).
- GET https://www.locally.com/headless/api/1.0/cart/items/add
- API REFERENCE: https://api.locally.com/reference/headlessapicontrollercart_add_item
BOPIS Request:
curl 'https://www.locally.com/headless/api/1.0/cart/items/add?store_id={{STORE_ID}}&upc={{UPC}}&host_domain={{HOST_DOMAIN}}' \
--header 'Locally-Api-Token: {{API_TOKEN}}'
Please provide the store_id
, UPC
, and host_domain
along with the required API token. For host_domain
, if the call originates from https://www.example.com, set the value as www.example.com
.
To flag an order for ROPIS, add the hold
parameter:
ROPIS Request:
curl 'https://www.locally.com/headless/api/1.0/cart/items/add?hold=true&store_id={{STORE_ID}}&upc={{UPC}}&host_domain={{HOST_DOMAIN}}' \
--header 'Locally-Api-Token: {{API_TOKEN}}'
For guidance on finding a store_id
for a given UPC, refer to this link. To view acquisition options for a store and UPC combination, study the data.markers[].acquisition_options
object in the aforementioned link's response.
Successful Response (HTTP Response == 200, success: true
):
{
"success": true,
"data": {
"n_in_cart": 1,
"cart_hash": "OV9",
"cart_content": {
"user": {
"id": 999,
"email": "anon-999.99@locally.com",
"first_name": "",
"last_name": "",
"phone": "",
"preferred_zip": "60605",
"preferred_country": "US"
},
"cart": {
"id": 999,
"user_id": 999,
"hash": "OV9",
"subtotal": 100,
"tax_total": 10.10,
"shipping_cost_total": 0,
"grand_total": 110.10,
"first_name": "",
"last_name": "",
"address": "",
"address_2": "",
"city": "",
"state": "",
"zip": "",
"email": "",
"phone": "",
"country": "",
"locale": "en-us",
"origin": "www.example.com",
"for_hold": 0,
"for_delivery": 0,
"delivery_first_name": "",
"delivery_last_name": "",
"delivery_address": "",
"delivery_address_2": "",
"delivery_city": "",
"delivery_state": "",
"delivery_zip": "",
"delivery_country": "",
"delivery_fee_total": 0,
"is_test": 0,
"delivery_choice": "",
"store_delivery_fee": 0,
"delivery_notes": "",
"coupon_discount_total": 0,
"coupon_code": "",
"coupon_discount_reason": "",
"for_ship_to_store": 0,
"cart_currency": "USD",
"items": [
{
"id": 999,
"qty": 1,
"upc": "000000000000",
"product_name": "ACME Outfitters Widget",
"product_company_name": "ACME Oufitters",
"product_image": "image.jpg",
"product_color": "Red",
"product_size": "M",
"product_price": 100,
"product_id": 999999,
"store_id": 99999,
"subtotal": 100,
"tax_total": 10.10,
"shipping_cost": 0,
"grand_total": 110.10,
"qty_available": 10,
"message": "",
"fulfillment_type": 0,
"product_currency": "USD",
"product_company_id": 31,
"coupon_discount_total": 0,
"vat": 0,
"tax_rate": 10.25,
"product": {
"id": 999999,
"name": "ACME Outfitters Widget",
"company_id": 31,
"style_number": "99999",
"image": "image.jpg",
"long_description": "Lorem ipsum.",
"average_rating": 5,
"n_reviews": 1,
"upc_data": null,
"company": null
}
}
],
"custom_cart_text": ""
},
"country": {
"id": 99,
"name": "United States",
"code": "US",
"primary_currency": "USD",
"secondary_currency": "",
"primary_locale": "en-us",
"phone_prefix": "1",
"display_marketing_consent": "",
"consent_checkbox_default_state": 0,
"code_3": "USA",
"display_brand_marketing_consent": ""
},
"cart_error": 0,
"cart_disclaimer": "By continuing you agree to our <a href=https:\/\/www.locally.com\/terms target=_blank>terms of service<\/a>",
"cart_store": {
"id": 99999,
"name": "ACME Oufitters Chicago",
"address": "123 ACME St.",
"zip": "00000",
"company_id": 99,
"lat": "41.91136867",
"lng": "-87.67771437",
"phone": "(555) 555-5555",
"mon_time_open": 1100,
"mon_time_close": 1900,
"tue_time_open": 1100,
"tue_time_close": 1900,
"wed_time_open": 1100,
"wed_time_close": 1900,
"thu_time_open": 1100,
"thu_time_close": 1900,
"fri_time_open": 1100,
"fri_time_close": 1900,
"sat_time_open": 1100,
"sat_time_close": 1900,
"sun_time_open": 1100,
"sun_time_close": 1700,
"address_2": "",
"city": "Chicago",
"state": "IL",
"country": "US",
"timezone": "America\/Chicago",
"average_rating": 5,
"n_reviews": 3,
"image": "image.jpg",
"web_address": "https:\/\/www.example.com",
"is_temporarily_closed": 0,
"area": null,
"store_hours": [
{
"id": 1,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 0,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 2,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 1,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 3,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 2,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 4,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 3,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 5,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 4,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 6,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 5,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
},
{
"id": 7,
"store_id": 99999,
"type": 0,
"custom_label": null,
"dow": 6,
"single_day": null,
"closed": 0,
"start_time": 700,
"end_time": 2000,
"group": 1,
"is_closed": 0
}
],
"pickup_time_slots": {
"time": null,
"days": null
},
"store_vat_details": {
"id": 99999,
"uses_vat": 0,
"sales_tax_label": ""
},
"acquisition_options": {
"bopis": 1,
"sdd": 1,
"ropis": 1
}
}
},
"delivery_options": null,
"cart_hash_jwt": "...",
"session": {
"id": "..."
}
},
"msg": ""
}
Unsuccessful Cart Response (HTTP Response == 200, success: false
):
{
"success": false,
"data": {
"...",
"cart_hash": "W9K",
"cart_content": {
"cart_error": "Sorry, this purchase option is no longer available at this store.",
},
"cart_hash_jwt": "...",
"session": {
"id": "..."
}
},
"msg": ""
}
Cart errors will return an HTTP response code of 200 but success
will be false
. When encountering errors specific to the cart, the response will include an explanation in data.cart_content.cart_error
.
Other Unsuccessful Response (HTTP Response >= 400, success: false
):
{
"success": false,
"data": {
"session": {
"id": "..."
}
},
"msg": "Undefined offset: 1",
"error_code": 0
}
Non cart errors will return an HTTP response code larger or equal to 400 and success
will be false
. An error description is provided in the msg
when possible.
Retrieving an existing cart
When creating a cart for the first time, note the following:
data.session.id
(session ID)data.cart_hash_jwt
(JSON Web Token or JWT).
The session ID can be redeemed at most endpoints and is useful for persisting a user's session between API calls; e.g. a cart or geographic location. The session ID can expire. Each endpoint payload will contain a session ID. If a session ID is passed to an endpoint and it is not expired, the same ID will be returned. If it is expired, a new ID will be returned.
The JWT uniquely identifies a user's cart and does not expire. It can be redeemed at cart endpoints to retrieve or update an existing cart and complete and order. It is optional, but recommended if long term persistence is required.
- GET https://www.locally.com/headless/api/1.0/cart
- API REFERENCE: https://api.locally.com/reference/headlessapicontrollercart_get_detail
Request:
curl 'https://www.locally.com/headless/api/1.0/cart' \
--header 'Locally-Api-Token: {{API_TOKEN}}' \
--header 'Locally-Api-Session-Id: {{SESSION_ID}}' \
--header 'Locally-Pl-Jwt: {{CART_HASH_JWT}}'
Successful Response:
A standard cart payload will be returned. Please review an example of a successful response, if needed.
Unsuccessful Response (HTTP Response == 404, success: false
):
{
"success": false,
"data": {
"session": {
"id": "..."
}
},
"msg": "Authentication error: User not found.",
"error_code": 1206
}
When a cart cannot be found, an HTTP 404 response with error_code
1206 and a msg
string will be returned. success
will be false
.
Updating a cart
By utilizing a session ID or JWT that belongs to an existing cart, we can update the quantity for items in the cart. Read more about a cart's session ID and JWT.
- POST https://www.locally.com/headless/api/1.0/cart
- API REFERENCE: https://api.locally.com/reference/headlessapicontrollercart_update
We can update the quantity for a specific item in the cart by specifying its ID along with the desired quantity. You can also remove an item by specifying the ID to remove. You can find the item's ID by looking for data.cart_content.cart.items[].id
in a successful cart response.
Updating cart quantity
Request:
curl 'https://www.locally.com/headless/api/1.0/cart?qty[{{ITEM_ID}}]={{QUANTITY}}&host_domain={{HOST_DOMAIN}}' \
-X 'POST' \
--header 'Locally-Api-Token: {{API_TOKEN}}' \
--header 'Locally-Api-Session-Id: {{SESSION_ID}}' \
--header 'Locally-Pl-Jwt: {{CART_HASH_JWT}}'
Successful Response:
A standard cart payload will be returned. Please review an example of a successful response, if needed.
Unsuccessful Response:
Please review an example of unsuccessful responses, if needed.
Removing an item from the cart
Request:
curl 'https://www.locally.com/headless/api/1.0/cart?remove_item={{ITEM_ID}}&host_domain={{HOST_DOMAIN}}' \
-X 'POST' \
--header 'Locally-Api-Token: {{API_TOKEN}}' \
--header 'Locally-Api-Session-Id: {{SESSION_ID}}' \
--header 'Locally-Pl-Jwt: {{CART_HASH_JWT}}'
Successful Response:
A standard cart payload will be returned. Please review an example of a successful response, if needed.
Unsuccessful Response:
Please review an example of unsuccessful responses, if needed.
Creating a BOPIS order
BOPIS orders require a reference to the customer's cart along with their email, name, marketing consent, address information and a Stripe setup intent ID.
The Stripe setup intent ID is constructed by retrieving the store's Stripe Connected ID and combining it with the market place secret key. Please contact Locally customer service for more information on obtaining a market place secret key during a custom integration.
All request types require the unique cart identifier. You can find the cart hash by looking for data.cart_hash
in a successful cart response. Please review an example of a successful response, if needed.
- POST https://www.locally.com/headless/api/1.0/cart/order
- API REFERENCE: https://api.locally.com/reference/headlessapicontrollercart_complete_order_request
BOPIS Request:
curl 'https://www.locally.com/headless/api/1.0/cart/order?hash={{CART_HASH}}&first_name={{FIRST_NAME}}&last_name={{LAST_NAME}}&full_name={{FIRST_NAME}}+{{LAST_NAME}}&email={{EMAIL}}&phone={{PHONE}}}&address={{ADDRESS}}&city={{CITY}}&state={{STATE}}&zip={{ZIP}}&country={{COUNTRY_CODE}}&is_opt_in_consent={{CONSENT_RETAILER}}&is_opt_in_consent_brand={{CONSENT_BRAND}}&setup_intent_id={{SETUP_INTENT_ID}}' \
-X 'POST' \
--header 'Locally-Api-Token: {{API_TOKEN}}' \
--header 'Locally-Api-Session-Id: {{SESSION_ID}}' \
--header 'Locally-Pl-Jwt: {{CART_HASH_JWT}}'
Successful BOPIS Response (HTTP Response == 200, success: true
):
{
"success": true,
"data": {
"message": "Your order has been placed",
"target_uri": "/order/.../...",
"status_code": 200,
"session": {
"id": "..."
}
},
"msg": ""
}
Unsuccessful Response (HTTP Response >= 400, success: false
):
{
"success": false,
"data": {
"session": {
"id": "..."
}
},
"msg": "We're sorry, we've detected that this item is no longer available and cannot be purchased.",
"error_code": 401
}
Any error will result in a non-200 HTTP response code. If possible, there will be an error description in the msg
field. success
will be false
.
Creating a ROPIS order
ROPIS orders require a reference to the customer's cart along with their email, name, phone number and marketing consent.
ROPIS request types require the unique cart identifier. You can find the cart hash by looking for data.cart_hash
in a successful cart response. Please review an example of a successful response, if needed.
ROPIS Request
curl 'https://www.locally.com/headless/api/1.0/cart/order?hash={{CART_HASH}}}&full_name={{FIRST_NAME}}+{{LAST_NAME}}&email={{EMAIL}}&phone={{PHONE}}}&is_opt_in_consent={{CONSENT_RETAILER}}&is_opt_in_consent_brand={{CONSENT_BRAND}}' \
-X 'POST' \
--header 'Locally-Api-Token: {{API_TOKEN}}' \
--header 'Locally-Api-Session-Id: {{SESSION_ID}}' \
--header 'Locally-Pl-Jwt: {{CART_HASH_JWT}}'
Successful ROPIS Response (HTTP Response == 200, success: true
):
{
"success": true,
"data": {
"message": "Your hold request has been sent.",
"target_uri": "/order/.../...",
"status": 200,
"status_code": 200,
"session": {
"id": "..."
}
},
"msg": ""
}
Unsuccessful Response (HTTP Response >= 400, success: false
):
{
"success": false,
"data": {
"session": {
"id": "..."
}
},
"msg": "We're sorry, we've detected that this item is no longer available and cannot be purchased.",
"error_code": 401
}
Any error will result in a non-200 HTTP response code. If possible, there will be an error description in the msg
field. success
will be false
.