Skip to content

Create or update a customer

For AI assistants: the facts every SparkLayer API call needs
  • Base URLs: Live https://app.sparklayer.io, test https://test.app.sparklayer.io. Each environment has its own data and its own API keys.
  • Access: API access needs the Pro or Enterprise plan. Create credentials (Site ID, Client ID, Client Secret) in the SparkLayer Dashboard at Settings > API.
  • Access token: POST {base}/api/auth/token with a Site-Id header and a JSON body (not form-encoded): {"grant_type":"client_credentials","client_id":"…","client_secret":"…"}. It returns access_token, valid for 3,600 seconds. There is no refresh token: cache the token and request a new one shortly before it expires.
  • Every request: Authorization: Bearer <access_token> and Site-Id: <site id>, plus Content-Type: application/json when there is a body. Set a User-Agent that names your integration.
  • Errors: RFC 7807 problem details: title, status, detail and an optional errors[] of { code, message, property }. Some error responses have no body. Retry 5xx, 429 (honouring Retry-After) and concurrent-update 409s with exponential backoff; on 401, get a new token once and retry; don't retry other 4xx without changing the request.
  • Pagination: Most list endpoints return everything. GET /api/v2/price-lists pages with page and page_size (up to 250) until pagination.current_page equals total_pages. GET /api/v1/purchases uses limit (up to 500) and offset: stop when a page has fewer than limit results.
  • Ground rules: Use only endpoints, fields and SDK methods that the docs or OpenAPI specs define. Prefer GET /api/v2/price-lists over the deprecated v1, and the Customers API (/api/v2/customers) over the legacy DELETE /api/v1/customers in the Core API. Products are matched by SKU. Keep credentials in environment variables or a secrets manager, never in code.
  • Read the docs as Markdown: Add .md to any page URL. Index of every page: docs.sparklayer.io/llms.txt. Every API operation, compactly: docs.sparklayer.io/llms-api.txt. The developer guides in full: docs.sparklayer.io/developers/llms.txt.
  • OpenAPI specs: customers, core, ordering, pricing, purchasing, stock, files, sync-log: https://docs.sparklayer.io/openapi/<api>.yaml (also .json). Ignite: https://docs.sparklayer.io/openapi/ignite.yaml.
  • MCP: Search and read these docs from your assistant with the docs MCP server at https://docs.sparklayer.io/mcp.
PUT
/api/v2/customers/{lookupBy}/{identifier}

Creates the customer if none matches lookupBy and identifier (201), or replaces the one that does (200).

  • The body must contain the identifier in the URL: external_id for platform, email for email, id for a SparkLayer UUID or legacy_id for a cu_ ID. Otherwise you get 400 (idMismatch). With sparklayer and a UUID no customer has, the customer is created with that ID.
  • Send the whole customer every time. Fields you leave out are cleared, including accounting_id, price_lists, discount_percentage, tax_exempt, sales_agent_groups and the credit limit, currency and net terms in payment_on_account. Addresses missing from addresses are deleted, so leaving addresses out deletes them all.
  • group and role must exist, and so must parent_customer_id if you set it, or you get 400. Price list slugs aren't checked, and sales agent groups that don't exist yet are created.
  • Emails and platform IDs are unique. 409 if another customer has the same email or external_id, or another address has the same address external_id.
  • payment_on_account records the balance. With balance_date, balance becomes an opening balance on that date. Without it, the balance is set to balance (or 0) as of now. See Adjust a customer's account balance.

To change only some fields, use Update a customer.

Authorization

bearerAuth
AuthorizationBearer <token>

Send Authorization: Bearer <access_token>, together with your Site-Id header, on every request. Get the token from POST /api/auth/token with your Site ID in the Site-Id header and a JSON body (not form-encoded, so standard OAuth 2.0 client libraries don't work): {"grant_type": "client_credentials", "client_id": "…", "client_secret": "…"}. Tokens are valid for 3,600 seconds and there is no refresh token: request a new one shortly before it expires. Create API credentials in the SparkLayer Dashboard under Settings > API. See Authentication.

In: header

Path Parameters

lookupBy*string

What identifier is: sparklayer (the customer's SparkLayer ID), platform (their ID on your eCommerce platform) or email (their email address).

Value in

  • "sparklayer"
  • "platform"
  • "email"
identifier*string

The customer's ID or email address, of the kind set by lookupBy. SparkLayer IDs are UUIDs; legacy cu_ IDs work too, except for shopping lists and favourites. Email lookups are exact and case-sensitive: URL-encode the address, with + as %2B.

Header Parameters

Site-Id*string

Your SparkLayer Site ID, from Settings > API in the SparkLayer Dashboard.

Length1 <= length

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

title?|

Title

Lengthlength <= 30
first_name*string

First Name

Lengthlength <= 128
last_name*string

Last Name

Lengthlength <= 128
company_name?|

Company Name

Lengthlength <= 255
parent_customer_id?|

SparkLayer or Platform Customer ID

Defaultnull
status*unknown

active customers can sign in. inactive customers are kept but can't sign in. archived marks a former B2B customer, such as one whose b2b tag was removed in Shopify: they're kept, but left out of searches and sales reps' customer lists.

Default"active"

Value in

  • "active"
  • "inactive"
  • "archived"
group*CustomerGroupSlug

Customer Group Slug

Length1 <= length <= 128
role*Role

A role that represents the users permissions

Lengthlength <= 64
default_billing_address_id?|

Default Billing Address ID

default_shipping_address_id?|

Default Shipping Address ID

available_shipping_addresses?array<>|

Limit the shipping addresses to be selected

sales_agent_groups?array<>

Sales Agent Groups The customer's own membership, and the only sales agent group list that is writable. Access decisions should read effective_sales_agent_groups, which also includes the groups the customer inherits from their section. Never write that field back here: it would turn a section's groups into the customer's own, and they would keep them after leaving the section. If a sales agent/customer, access is only given to other sales agents/customers with one of the groups specified. If a customer, access is only given to other sales agents with one of the groups specified. If empty for a customer, only sales agents with an empty list can access the customer. If empty for a sales agent, any customer is accessible.

price_lists?array<>

Price list slugs for this customer, which replace their customer group's. SparkLayer doesn't check the slugs: make sure each price list exists.

discount_percentage?|

Customer specific percentage discount

Formatfloat
Range0 <= value <= 100
addresses?array<|>
cart_uuid?|
Formatuuid
previous_cart_uuid?|
Formatuuid
cart_loaded_from_purchase?|
Defaultfalse
tax_exempt?boolean|null
id?string

SparkLayer Customer ID

Formatuuid
legacy_id?|

Legacy SparkLayer Customer ID

Length4 <= length
external_id*|

The customer's ID on your eCommerce platform. Look the customer up by it with lookupBy set to platform.

Length1 <= length <= 124
accounting_id?|

The identifier for the customer on the merchants accountancy or ERP

Length1 <= length <= 124
email*string

The customer's email address. Lookups by email are exact and case-sensitive.

Length1 <= length <= 255
payment_on_account?||null

Response Body

application/json

application/json

application/problem+json

curl -X PUT "https://app.sparklayer.io/api/v2/customers/platform/7843024560291" \  -H "Authorization: Bearer <ACCESS_TOKEN>" \  -H "Site-Id: jones-climbing" \  -H "Content-Type: application/json" \  -d '{    "title": "Mr",    "first_name": "Tom",    "last_name": "Jones",    "company_name": "Tom Jones Climbing Ltd",    "parent_customer_id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "status": "active",    "group": "base",    "role": "limited",    "default_billing_address_id": "<default_billing_address_id>",    "default_shipping_address_id": "<default_shipping_address_id>",    "available_shipping_addresses": [      "<available_shipping_addresses>"    ],    "sales_agent_groups": [      "south-west-uk"    ],    "price_lists": [      "b2b_gb_gbp_123"    ],    "discount_percentage": 0,    "addresses": [      {        "id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",        "legacy_id": "ca_47",        "title": "Mr",        "first_name": "Bob",        "last_name": "Jones",        "company": "Tom Jones Climbing Ltd",        "address_line1": "Example Industrial Estate",        "address_line2": "North Country",        "city": "Cityland",        "region_code": "CA",        "postal_code": "12345",        "country_code": "US",        "phone": "+44 (0) 123456789",        "external_id": "123456789",        "is_default_shipping": true,        "is_default_billing": true,        "valid_for_purchase_id": "3f2b8c1e-5d4a-4e6f-9b7c-1a2d3e4f5a6b"      }    ],    "cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "previous_cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "cart_loaded_from_purchase": false,    "tax_exempt": true,    "id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "legacy_id": "cu_123456789",    "external_id": "123456789",    "accounting_id": "<accounting_id>",    "email": "Tom.jones@climbing.com",    "payment_on_account": null  }'
{  "title": "Mr",  "first_name": "Tom",  "last_name": "Jones",  "company_name": "Tom Jones Climbing Ltd",  "status": "active",  "group": "base",  "default_billing_address_id": "string",  "default_shipping_address_id": "string",  "available_shipping_addresses": [    "string"  ],  "sales_agent_groups": [    "south-west-uk"  ],  "price_lists": [    "b2b_gb_gbp_123"  ],  "discount_percentage": 0,  "addresses": [    {      "id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",      "legacy_id": "ca_47",      "created_at": "2019-08-24T14:15:22Z",      "updated_at": "2019-08-24T14:15:22Z",      "title": "Mr",      "first_name": "Bob",      "last_name": "Jones",      "company": "Tom Jones Climbing Ltd",      "address_line1": "Example Industrial Estate",      "address_line2": "North Country",      "city": "Cityland",      "region_name": "California",      "region_code": "CA",      "postal_code": "12345",      "country_name": "USA",      "country_code": "US",      "phone": "+44 (0) 123456789",      "external_id": "123456789",      "is_default_shipping": true,      "is_default_billing": true,      "valid_for_purchase_id": "64bd7a6d-6c6e-4483-b017-db631805fab6"    }  ],  "cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",  "previous_cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",  "cart_loaded_from_purchase": false,  "tax_exempt": true,  "id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",  "legacy_id": "cu_123456789",  "external_id": "123456789",  "accounting_id": "string",  "email": "Tom.jones@climbing.com",  "effective_sales_agent_groups": [    "south-west-uk"  ],  "role": "limited",  "role_object": {    "id": "019bbd11-4e64-735f-9205-5b7bc84181f6",    "name": "My Custom Role",    "slug": "my-custom-role",    "type": "customer",    "permissions": {      "purchase-edit-prices": true,      "customers-create-new": false    },    "is_default": true  },  "parent_customer_id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",  "created_at": "2019-08-24T14:15:22Z",  "updated_at": "2019-08-24T14:15:22Z",  "parent_customer": {    "title": "Mr",    "first_name": "Tom",    "last_name": "Jones",    "company_name": "Tom Jones Climbing Ltd",    "parent_customer_id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "status": "active",    "group": "base",    "role": "limited",    "default_billing_address_id": "string",    "default_shipping_address_id": "string",    "available_shipping_addresses": [      "string"    ],    "sales_agent_groups": [      "south-west-uk"    ],    "price_lists": [      "b2b_gb_gbp_123"    ],    "discount_percentage": 0,    "addresses": [      {        "id": "9151f21f-43ae-43b4-92f3-f4af67cdf544",        "legacy_id": "ca_47",        "created_at": "2019-08-24T14:15:22Z",        "updated_at": "2019-08-24T14:15:22Z",        "title": "Mr",        "first_name": "Bob",        "last_name": "Jones",        "company": "Tom Jones Climbing Ltd",        "address_line1": "Example Industrial Estate",        "address_line2": "North Country",        "city": "Cityland",        "region_name": "California",        "region_code": "CA",        "postal_code": "12345",        "country_name": "USA",        "country_code": "US",        "phone": "+44 (0) 123456789",        "external_id": "123456789",        "is_default_shipping": true,        "is_default_billing": true,        "valid_for_purchase_id": "64bd7a6d-6c6e-4483-b017-db631805fab6"      }    ],    "cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "previous_cart_uuid": "9151f21f-43ae-43b4-92f3-f4af67cdf544",    "cart_loaded_from_purchase": false,    "tax_exempt": true  },  "payment_on_account": null}