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

EnvironmentURL
Productionhttps://production-api.shoutaboutus.com
Developmenthttps://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_id rule. It must be a company row with bundle_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):
FieldTypeRequiredDescription
account_idintegeryesMust be an existing, non-deleted account, and belong to the auth user's company hierarchy (an ownership check).
location_filefile (xlsx/xls)yesmimetypes: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 when status=error) — map keyed by spreadsheet row number (data row index + 2) to an array of error message strings.

200 OK · 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

{
  "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

{
  "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

{
  "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

StatusMeaning
400Bad Header / Empty File
401The bearer token is missing, expired or invalid
422Validation Error
500Unexpected 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'

Did this page help you?