Overview
The Location Management API gives you programmatic control over the location data behind your locator. Build a sync pipeline from your existing systems and updates flow through on their own. With it you can:
- Retrieve paginated lists of locations
- Get detailed information for a specific location
- Create new locations
- Update existing location data
- Delete locations
- Read your custom field configuration
This API writes data. Two ways to display it. Locations you sync here power the embedded widget as a drop-in UI, and the Location Query API when you build a custom frontend. Both read the same data.
Authentication
Every endpoint requires a private API key sent as a Bearer token in the Authorization header:
Authorization: Bearer sk_your_api_key
Generate a private API key in the Developer Portal in your Storepoint dashboard. Keys start with sk_. The same page lets you rotate or revoke keys at any time.
Keep this key secret. It grants full administrative access to your locator and locations. Use it from your server only. Keep it out of frontend code, public repositories, and client-side applications. If a key is ever exposed, rotate it from the dashboard right away.
Security best practices
- Call this API from your backend. Frontend applications should use the Location Query API, which is built for the browser.
- Store the API key in backend environment variables or a secrets manager.
- Rotate your API key immediately if it is ever exposed.
- Share key access with trusted administrators only.
- Monitor API usage in the Developer Portal and watch for suspicious activity.
Endpoints
Six endpoints cover the full lifecycle: list, read, create, update, delete, and your custom field configuration.
Base URL
All endpoint paths below are relative to:
https://api.storepoint.co/admin/v1
GET /locations
Retrieves a paginated list of all locations, published and draft, for the authenticated locator.
Query parameters
All parameters are optional.
| Parameter | Type | Description |
|---|---|---|
limit |
integer | Results per page. Default 10, maximum 500. |
page |
integer | Page number. Default 1. |
status |
string | Filter by status: published or draft. Omit to get both. |
Example request
curl "https://api.storepoint.co/admin/v1/locations?limit=50&page=1" \
-H "Authorization: Bearer sk_your_api_key"
Example response
{
"success": true,
"locations": [
{
"id": "loc_abc123",
"name": "Store Name",
"address": "123 Main St",
"coordinates": {
"lat": 12.345678,
"lng": -12.345678
},
"description": "Store description",
"tags": ["tag1", "tag2"],
"contact": {
"phone": "555-1234",
"email": "[email protected]",
"website": "https://example.com"
},
"social": {
"instagram": "https://instagram.com/downtownstore",
"facebook": "",
"twitter": ""
},
"hours": {
"monday": "9:00-17:00",
"tuesday": "9:00-17:00",
"wednesday": "9:00-17:00",
"thursday": "9:00-17:00",
"friday": "9:00-17:00",
"saturday": "10:00-15:00",
"sunday": "closed"
},
"custom_fields": {},
"image_url": "https://example.com/image.jpg",
"status": "published"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"total_pages": 10,
"next": "/admin/v1/locations?page=2",
"previous": null
}
}
GET /locations/{id}
Retrieves detailed information for a specific location. Returns both published and draft locations.
Example response
{
"success": true,
"location": {
"id": "loc_abc123",
"name": "Store Name",
"address": "123 Main St",
"coordinates": {
"lat": 12.345678,
"lng": -12.345678
},
"description": "Store description",
"tags": ["tag1", "tag2"],
"contact": {
"phone": "555-1234",
"email": "[email protected]",
"website": "https://example.com"
},
"social": {
"instagram": "https://instagram.com/downtownstore",
"facebook": "",
"twitter": ""
},
"hours": {
"monday": "9:00-17:00",
"tuesday": "9:00-17:00",
"wednesday": "9:00-17:00",
"thursday": "9:00-17:00",
"friday": "9:00-17:00",
"saturday": "10:00-15:00",
"sunday": "closed"
},
"custom_fields": {},
"image_url": "https://example.com/image.jpg",
"status": "published"
}
}
POST /locations
Creates a new location. Locations are published by default unless status is set to draft.
Coordinates are optional. We geocode for you. Send an address and Storepoint converts it to coordinates automatically. Include a coordinates object when you already have exact coordinates and want to use those instead.
Body fields
| Field | Type | Description |
|---|---|---|
name |
string | Location name. |
address |
string | Full address. Storepoint geocodes it automatically when coordinates is omitted. |
coordinates |
object | Optional. lat and lng in decimal degrees. When provided, your coordinates are used as-is. |
description |
string | Description shown on the location. |
tags |
array | Tags for filtering. Matched case-insensitively, returned in the case you send. |
contact |
object | phone, email, and website. |
social |
object | instagram, facebook, and twitter profile URLs. Always present in responses. |
hours |
object | Opening hours keyed by day. Any text string is accepted. |
custom_fields |
object | Custom field values keyed by field ID. See custom fields. |
image_url |
string | Location photo URL, including the protocol. |
status |
string | published or draft. Defaults to published. |
Example request
This request sends an address with no coordinates. Storepoint geocodes it and the response includes the resulting coordinates.
{
"name": "Store Name",
"address": "123 Main St",
"description": "Store description",
"tags": ["tag1", "tag2"],
"contact": {
"phone": "555-1234",
"email": "[email protected]",
"website": "https://example.com"
},
"hours": {
"monday": "9:00-17:00",
"tuesday": "9:00-17:00",
"wednesday": "9:00-17:00",
"thursday": "9:00-17:00",
"friday": "9:00-17:00",
"saturday": "10:00-15:00",
"sunday": "closed"
},
"custom_fields": {
"cf812sj12": "Free parking",
"feu3hsw32": "yes",
"hs2372a42": "Available"
},
"image_url": "https://example.com/store-photo.jpg",
"status": "published"
}
Example response
{
"success": true,
"location": {
"id": "loc_abc123",
"name": "Store Name",
"address": "123 Main St",
"coordinates": {
"lat": 12.345678,
"lng": -12.345678
},
"description": "Store description",
"tags": ["tag1", "tag2"],
"contact": {
"phone": "555-1234",
"email": "[email protected]",
"website": "https://example.com"
},
"social": {
"instagram": "https://instagram.com/downtownstore",
"facebook": "",
"twitter": ""
},
"hours": {
"monday": "9:00-17:00",
"tuesday": "9:00-17:00",
"wednesday": "9:00-17:00",
"thursday": "9:00-17:00",
"friday": "9:00-17:00",
"saturday": "10:00-15:00",
"sunday": "closed"
},
"custom_fields": {
"cf812sj12": "Free parking",
"feu3hsw32": "yes",
"hs2372a42": "Available"
},
"image_url": "https://example.com/store-photo.jpg",
"status": "published"
}
}
POST /locations/{id}
Updates an existing location. All fields are optional. Only the fields you send are updated. Works with both published and draft locations.
When you update the address, Storepoint re-geocodes it automatically. Send a coordinates object to set coordinates yourself instead.
Example request
{
"name": "Updated Store Name",
"address": "123 Main St",
"description": "Updated description",
"tags": ["updated_tag"],
"contact": {
"phone": "555-5678",
"email": "[email protected]",
"website": "https://updated.com"
},
"hours": {
"monday": "9:00-18:00",
"tuesday": "9:00-18:00",
"wednesday": "9:00-18:00",
"thursday": "9:00-18:00",
"friday": "9:00-18:00",
"saturday": "10:00-16:00",
"sunday": "closed"
},
"custom_fields": {
"cf812sj12": "Updated parking info",
"feu3hsw32": "no",
"hs2372a42": "Not available"
},
"image_url": "https://example.com/updated-photo.jpg",
"status": "published"
}
Example response
{
"success": true,
"location": {
"id": "loc_abc123",
"name": "Updated Store Name",
"address": "123 Main St",
"coordinates": {
"lat": 12.345678,
"lng": -12.345678
},
"description": "Updated store description",
"tags": ["updated_tag"],
"contact": {
"phone": "555-5678",
"email": "[email protected]",
"website": "https://updated-example.com"
},
"social": {
"instagram": "https://instagram.com/downtownstore",
"facebook": "",
"twitter": ""
},
"hours": {
"monday": "9:00-18:00",
"tuesday": "9:00-18:00",
"wednesday": "9:00-18:00",
"thursday": "9:00-18:00",
"friday": "9:00-18:00",
"saturday": "10:00-16:00",
"sunday": "closed"
},
"custom_fields": {
"cf812sj12": "Updated parking info",
"feu3hsw32": "no",
"hs2372a42": "Not available"
},
"image_url": "https://example.com/updated-store-photo.jpg",
"status": "published"
}
}
POST /locations/{id}/delete
Deletes a location. Works with both published and draft locations.
Example response
{
"success": true,
"message": "Location deleted successfully"
}
GET /custom_fields
Retrieves the custom fields configured for your locator. Each field has an id. Use those IDs to identify or set custom fields when getting, creating, or updating locations.
Example response
{
"success": true,
"custom_fields": [
{
"id": "cf812sj12",
"name": "Field Name",
"type": "text"
}
]
}
Using custom fields. When creating or updating locations, use the field IDs as keys in the custom_fields object. For example: "custom_fields": { "cf812sj12": "Free street parking", "feu3hsw32": "yes" }
Error responses
All endpoints may return the following error responses.
Authentication error (401)
Returned when the API key is missing, invalid, inactive, or expired. The message explains which.
{
"error": "Authentication failed",
"message": "Invalid or inactive token"
}
Invalid request (400)
Returned for bad request shapes: an invalid limit or page, a limit above 500 (limit_exceeded), a missing location ID, or an update with no valid fields.
{
"error": "invalid_request",
"message": "No valid fields provided for update"
}
Geocoding error (422)
Returned when you send an address without coordinates and the address can't be geocoded. Fix the address or include a coordinates object.
{
"error": "geocoding_failed",
"message": "Unable to geocode the provided address. Please provide coordinates or verify the address."
}
Validation error (422)
{
"error": "validation_error",
"message": "Invalid input data",
"details": {
"field_name": ["Error message"]
}
}
Not found error (404)
{
"error": "not_found",
"message": "Location not found"
}
Server error (500)
{
"error": "error",
"message": "Error message"
}
Technical notes
- All location IDs are prefixed with
loc_followed by random lowercase alphanumeric characters. - Coordinates are optional. Send an address and Storepoint geocodes it automatically. When you provide coordinates, use decimal degrees.
- Hours can be provided as any text string. Common formats like 24-hour time ranges (
HH:MM-HH:MM) orclosedare recommended for consistency but are open to whatever suits your data. - Tags are matched case-insensitively and returned in the case you send them. You can organize tags into filters and groups in the Tags & Filters area of your dashboard.
- Custom field keys must match the field IDs returned by the custom fields endpoint.
- All URLs (
website,image_url,socialprofiles) must be full URLs including the protocol (https://). - Status: locations are either
published(visible on your store locator) ordraft(hidden from the public locator). New locations default topublishedif not specified. Legacy valueshidden,inactive, andunpublishedare accepted and treated asdraft.