Create or update a customer
For AI assistants: the facts every SparkLayer API call needs
- Base URLs: Live
https://app.sparklayer.io, testhttps://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/tokenwith aSite-Idheader and a JSON body (not form-encoded):{"grant_type":"client_credentials","client_id":"…","client_secret":"…"}. It returnsaccess_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>andSite-Id: <site id>, plusContent-Type: application/jsonwhen there is a body. Set aUser-Agentthat names your integration. - Errors: RFC 7807 problem details:
title,status,detailand an optionalerrors[]of{ code, message, property }. Some error responses have no body. Retry5xx,429(honouringRetry-After) and concurrent-update409s with exponential backoff; on401, get a new token once and retry; don't retry other4xxwithout changing the request. - Pagination: Most list endpoints return everything.
GET /api/v2/price-listspages withpageandpage_size(up to 250) untilpagination.current_pageequalstotal_pages.GET /api/v1/purchasesuseslimit(up to 500) andoffset: stop when a page has fewer thanlimitresults. - Ground rules: Use only endpoints, fields and SDK methods that the docs or OpenAPI specs define. Prefer
GET /api/v2/price-listsover the deprecated v1, and the Customers API (/api/v2/customers) over the legacyDELETE /api/v1/customersin 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
.mdto 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} Customers API
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_idforplatform,emailforemail,idfor a SparkLayer UUID orlegacy_idfor acu_ID. Otherwise you get400(idMismatch). Withsparklayerand 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_groupsand the credit limit, currency and net terms inpayment_on_account. Addresses missing fromaddressesare deleted, so leavingaddressesout deletes them all. groupandrolemust exist, and so mustparent_customer_idif you set it, or you get400. Price list slugs aren't checked, and sales agent groups that don't exist yet are created.- Emails and platform IDs are unique.
409if another customer has the sameemailorexternal_id, or another address has the same addressexternal_id. payment_on_accountrecords the balance. Withbalance_date,balancebecomes an opening balance on that date. Without it, the balance is set tobalance(or 0) as of now. See Adjust a customer's account balance.
To change only some fields, use Update a customer.
Authorization
bearerAuth 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
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"
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
Your SparkLayer Site ID, from Settings > API in the SparkLayer Dashboard.
1 <= lengthRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Title
length <= 30First Name
length <= 128Last Name
length <= 128Company Name
length <= 255SparkLayer or Platform Customer ID
nullactive 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.
"active"Value in
- "active"
- "inactive"
- "archived"
Customer Group Slug
1 <= length <= 128A role that represents the users permissions
length <= 64Default Billing Address ID
Default Shipping Address ID
Limit the shipping addresses to be selected
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 list slugs for this customer, which replace their customer group's. SparkLayer doesn't check the slugs: make sure each price list exists.
Customer specific percentage discount
float0 <= value <= 100uuiduuidfalseSparkLayer Customer ID
uuidLegacy SparkLayer Customer ID
4 <= lengthThe customer's ID on your eCommerce platform. Look the customer up by it with lookupBy set to platform.
1 <= length <= 124The identifier for the customer on the merchants accountancy or ERP
1 <= length <= 124The customer's email address. Lookups by email are exact and case-sensitive.
1 <= length <= 255Response 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}