Skip to main content

Developer reference

Custom endpoint

Custom endpoints let you offer pickup points from any source not covered by our built-in carrier integrations. This feature requires Shopify Plus and a developer to implement the endpoint.

#How it works

In Shopify Admin → Atlas Pickup Points, open the pickup point configuration, select Custom as the carrier, and paste your endpoint URL. Each time pickup points are displayed, Atlas makes a GET request to your endpoint with the following query parameters:

Parameter Name Description
lat Latitude Latitude of the buyer’s shipping address or their position on the map
lng Longitude Longitude of the buyer’s shipping address or their position on the map
country_code Country code Country code (ISO 3166-1 alpha-2) of the buyer’s shipping address. Does not change if the buyer moves the map to a different country.
q Search query Optional: search query from the list view. Search can be enabled in the custom endpoint settings.

If your endpoint requires additional options, you can pass them as query parameters and they will be merged with the parameters above.

#Authentication

If your endpoint requires authentication, you can add a secret in the custom endpoint settings. Atlas will send it as the x-atlas-pickup-points-secret header with every request, allowing your endpoint to verify that the request is coming from Atlas.

The endpoint URL must serve the response directly. Redirects (3xx responses) are not followed, so it must point to the final location.

Your endpoint must respond with JSON in the following format:

{
  "locations": [
    {
      "code": "019889",
      "address": {
        "address1": "19 BOULEVARD GALLIENI",
        "address2": null,
        "city": "NEUILLY PLAISANCE",
        "zip": "93360",
        "country_code": "FR",
        "latitude": 48.8532417,
        "longitude": 2.514444
      },
      "details": {
        "name": "NEW HIGH TECH GSM",
        "description": null,
        "business_hours": [
          {
            "day": "MONDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "TUESDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "WEDNESDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "THURSDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "FRIDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "SATURDAY",
            "opening_time": "08:00",
            "closing_time": "21:00"
          },
          {
            "day": "SUNDAY",
            "opening_time": "09:00",
            "closing_time": "20:00"
          }
        ],
        "open_24_hours": false
      },
      "attributes": [
        {
          "key": "type",
          "value": "PUDO"
        }
      ]
    },
    {
      "code": "014514",
      "address": {
        "address1": "32 RUE ROGER-SALENGRO",
        "address2": null,
        "city": "FONTENAY SOUS BOIS",
        "zip": "94120",
        "country_code": "FR",
        "latitude": 48.855606,
        "longitude": 2.474787
      },
      "details": {
        "name": "LOCKER 24/7 INTERMARCH ROGER SA",
        "description": null,
        "business_hours": [],
        "open_24_hours": true
      },
      "attributes": [
        {
          "key": "type",
          "value": "LOCKER"
        }
      ]
    }
  ]
}

#Response

#Response object

Key Required Type Description
locations true Location[] The pickup points near the requested lat/lng. Required even when error_message is set; return [] when there are no nearby points.
error_message false String Set to a non-empty string to fail the request: Atlas returns error code CUSTOM_INTEGRATION_CUSTOM_ERROR with this message instead of pickup points. Visible in the network response for debugging, never shown to the buyer. Omit or null on success.

#Location object

Key Required Type Description
code true String Your stable identifier for this pickup point. Sent back to you verbatim when the buyer selects it.
address true Address Pickup point address.
details true Details Pickup point details.
attributes false Attributes[] Optional key/value pairs stored with the buyer’s selection and passed to the order.
icon_url false String Optional URL of a custom map pin icon for this point on the checkout map. Should be a PNG hosted on the Shopify CDN.

#Address object

Key Required Type Description
longitude true Number Longitude of the pickup point in decimal degrees.
latitude true Number Latitude of the pickup point in decimal degrees.
address1 true String First line of the address (street and number), displayed to the buyer.
address2 false String Second line of the address. Not displayed to the buyer at checkout.
city true String City, displayed to the buyer.
zip true (except RO) String Postal code. Required except in countries without postal codes (currently RO).
country_code true String ISO 3166-1 alpha-2 country code. Must match the country_code query parameter of the request; locations in other countries are dropped. A code Atlas does not recognize fails the whole response.

#Details object

Key Required Type Description
name true String Name of the pickup point, displayed to the buyer.
description false String Optional description of the location, displayed to the buyer.
business_hours false BusinessHour[] Opening hours displayed to the buyer. Send an empty array when hours are unknown or the point is open 24/7.
open_24_hours false Boolean Set true if the point is open 24/7; business_hours is then ignored. False, null, and omitted are equivalent.

#BusinessHour object

Key Required Type Description
day true Enum: MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY Day of the week.
opening_time true String Opening time, 24-hour HH:MM recommended (e.g. “08:00”).
closing_time true String Closing time, 24-hour HH:MM recommended (e.g. “19:30”).

#Attributes object

Key Required Type Description
key true String Key of the attribute.
value true String / Number / Boolean Value of the attribute.

#JSON Schema

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Atlas Pickup Points custom endpoint response",
  "description": "The JSON body your custom pickup-point endpoint must return to Atlas Pickup Points. Atlas calls your URL with GET and the query parameters lat, lng, country_code, optionally q (the buyer's list-search input) and checkout_token, plus your configured secret in the x-atlas-pickup-points-secret header. Respond with HTTP 200 and this shape, returning only locations in the requested country. Documentation: https://atlaspickuppoints.com/docs/developer-reference/custom-endpoint",
  "type": "object",
  "required": ["locations"],
  "properties": {
    "locations": {
      "type": "array",
      "description": "The pickup points near the requested lat/lng. Required even when error_message is set; return [] when there are no nearby points.",
      "items": {
        "$ref": "#/definitions/location"
      }
    },
    "error_message": {
      "type": ["string", "null"],
      "description": "Set to a non-empty string to fail the request: Atlas returns error code CUSTOM_INTEGRATION_CUSTOM_ERROR with this message instead of pickup points. Visible in the network response for debugging, never shown to the buyer. Omit or null on success."
    }
  },
  "definitions": {
    "location": {
      "type": "object",
      "required": ["code", "address", "details"],
      "properties": {
        "code": {
          "type": "string",
          "minLength": 1,
          "description": "Your stable identifier for this pickup point. Sent back to you verbatim when the buyer selects it."
        },
        "address": {
          "$ref": "#/definitions/address",
          "description": "Pickup point address."
        },
        "details": {
          "$ref": "#/definitions/details",
          "description": "Pickup point details."
        },
        "attributes": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/attribute"
          },
          "description": "Optional key/value pairs stored with the buyer's selection and passed to the order."
        },
        "icon_url": {
          "type": ["string", "null"],
          "description": "Optional URL of a custom map pin icon for this point on the checkout map. Should be a PNG hosted on the Shopify CDN."
        }
      }
    },
    "address": {
      "type": "object",
      "required": ["longitude", "latitude", "address1", "city", "country_code"],
      "properties": {
        "longitude": {
          "type": "number",
          "minimum": -180,
          "maximum": 180,
          "description": "Longitude of the pickup point in decimal degrees."
        },
        "latitude": {
          "type": "number",
          "minimum": -90,
          "maximum": 90,
          "description": "Latitude of the pickup point in decimal degrees."
        },
        "address1": {
          "type": "string",
          "pattern": "\\S",
          "description": "First line of the address (street and number), displayed to the buyer."
        },
        "address2": {
          "type": ["string", "null"],
          "description": "Second line of the address. Not displayed to the buyer at checkout."
        },
        "city": {
          "type": "string",
          "pattern": "\\S",
          "description": "City, displayed to the buyer."
        },
        "zip": {
          "type": "string",
          "description": "Postal code. Required except in countries without postal codes (currently RO)."
        },
        "country_code": {
          "type": "string",
          "pattern": "^[A-Z]{2}$",
          "description": "ISO 3166-1 alpha-2 country code. Must match the country_code query parameter of the request; locations in other countries are dropped. A code Atlas does not recognize fails the whole response."
        }
      },
      "anyOf": [
        {
          "properties": {
            "country_code": {
              "enum": ["RO"]
            }
          },
          "required": ["country_code"],
          "description": "RO pickup points may omit or blank the zip"
        },
        {
          "required": ["zip"],
          "properties": {
            "zip": {
              "pattern": "\\S"
            }
          }
        }
      ]
    },
    "details": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": {
          "type": "string",
          "pattern": "\\S",
          "description": "Name of the pickup point, displayed to the buyer."
        },
        "description": {
          "type": ["string", "null"],
          "description": "Optional description of the location, displayed to the buyer."
        },
        "business_hours": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/businessHour"
          },
          "description": "Opening hours displayed to the buyer. Send an empty array when hours are unknown or the point is open 24/7."
        },
        "open_24_hours": {
          "type": ["boolean", "null"],
          "description": "Set true if the point is open 24/7; business_hours is then ignored. False, null, and omitted are equivalent."
        }
      }
    },
    "businessHour": {
      "type": "object",
      "required": ["day", "opening_time", "closing_time"],
      "properties": {
        "day": {
          "description": "Day of the week.",
          "enum": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]
        },
        "opening_time": {
          "type": "string",
          "minLength": 1,
          "description": "Opening time, 24-hour HH:MM recommended (e.g. \"08:00\")."
        },
        "closing_time": {
          "type": "string",
          "minLength": 1,
          "description": "Closing time, 24-hour HH:MM recommended (e.g. \"19:30\")."
        }
      }
    },
    "attribute": {
      "type": "object",
      "required": ["key", "value"],
      "properties": {
        "key": {
          "type": "string",
          "minLength": 1,
          "description": "Key of the attribute."
        },
        "value": {
          "type": ["string", "number", "boolean"],
          "description": "Value of the attribute."
        }
      }
    }
  }
}

#Implementation tips

#Hosting options

We recommend Heroku, AWS Lambda, or Cloudflare Workers. For the best performance and lowest latency, deploy in the EU region, preferably AWS eu-west-1 (Ireland), where our infrastructure is located.

For static locations (up to 50):

  1. Create a JSON file with your locations
  2. Upload it in Shopify Admin → Content → Files
  3. Use the CDN URL as your endpoint

#Troubleshooting

If you receive a CUSTOM_INTEGRATION_INVALID_RESPONSE error:

  1. Open your browser’s Developer Tools
  2. Go to the Network tab
  3. Find the request to the /locations endpoint
  4. Inspect the response to see detailed error information

While we don’t assist with implementation, our support team is happy to help with debugging issues.