Bulk Upload Locations (request verification)
Overview
Accepts an Excel file of locations and an account_id (account), then validates the header row and every data row. Stores the file to S3, and emails the admin team to verify before any locations are created. Does NOT create locations itself.
Prerequisites
- A bearer token. Callable with Partner and Account tokens.
Base URL
| Environment | URL |
|---|---|
| Production | https://production-api.shoutaboutus.com |
| Development | https://development-api.shoutaboutus.com |
Endpoint
POST /api/v1/bulk-upload/locations
Authentication
-
Requires a bearer token in the
Authorization: Bearer <bearer-token>header. -
Who can call it: Partner and Account tokens.
-
Lives in the top-level authenticated group (no
partner/*,admin/*, or partner-only access). -
The endpoint scopes the target by the
account_idrule. It must be acompanyrow withbundle_id=3(Account), not soft-deleted, and pass an ownership check. The ownership check walks the auth user's company hierarchy. -
A Partner (
bundle_id=1) can target any Account under their tree. -
An Account user can target Accounts in their own hierarchy.
Rate limit
- No rate limit.
Request body
- Body (
multipart/form-data):
| Field | Type | Required | Description |
|---|---|---|---|
account_id | integer | yes | Must be an existing, non-deleted account, and belong to the auth user's company hierarchy (an ownership check). |
location_file | file (xlsx/xls) | yes | mimetypes:application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, max 10240 KB. Header row must exactly match the template: location_name, store_id, gbp_name, website, address, city, state, zip, country_code, business_phone, location_user_first_name_optional, location_user_last_name_optional, location_user_email_address_optional, location_user_phone_number_optional. |
The endpoint validates each row: address, city, state, zip, business_phone (9-10 digits, no formatting), country_code (must be a known country code), and location_name are required. gbp_name, location_user_phone_number_optional, location_user_email_address_optional (valid email, unique in users, distinct) are optional. Formula values (#NAME?) are rejected.
Response
data.status(string) —"success"on accepted upload,"error"when per-row validation fails.data.message(string) — human-readable status text.data.errors(object, only whenstatus=error) — map keyed by spreadsheet row number (data row index + 2) to an array of error message strings.
200 OK · 200
200{
"data": {
"status": "success",
"message": "Thank you. Upload complete. We will confirm with you once data and locations are live."
}
}200 Row Validation Errors · 200
200{
"data": {
"message": "Please correct the errors and re-upload the file.",
"status": "error",
"errors": {
"3": [
"The address field is required.",
"Country code must be a valid country code."
],
"5": [
"The Business phone field is required. Do not enter ( ) or - in the phone number field Please only enter the 10 digit numbers with no country code."
]
}
}
}400 Bad Header / Empty File · 400
400{
"message": "Bad Request",
"status": "error",
"errors": {
"location_file": "The Excel file is invalid. The header names and order must match those in the template file."
}
}422 Validation Error · 422
422{
"message": "The account is invalid or deleted.",
"errors": {
"account_id": [
"The account is invalid or deleted."
],
"location_file": [
"The location file field must be a file of type Excel"
]
}
}Errors
| Status | Meaning |
|---|---|
400 | Bad Header / Empty File |
401 | The bearer token is missing, expired or invalid |
422 | Validation Error |
500 | Unexpected server error |
Example request
curl --request POST \
--url "https://production-api.shoutaboutus.com/api/v1/bulk-upload/locations" \
--header 'Authorization: Bearer <bearer-token>' \
--header 'Accept: application/json' \
--form 'account_id=123' \
--form 'location_file=@location_file.xlsx'Updated 9 days ago
