Stitchflow
Shopify logo

Shopify User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 5, 2026

Summary and recommendation

Shopify's Admin API splits user management across two distinct surfaces that must not be conflated. The REST Customer API (base URL: https://{store}.myshopify.com/admin/api/2024-01) manages storefront buyers - not staff or admin users.

Staff management requires either the GraphQL StaffMember type (read_staff_members / write_staff_members scopes, Plus org-level only) or the SCIM 2.0 endpoint (https://{organization}.myshopify.com/scim/v2, Plus + SAML prerequisite). Auth is OAuth 2.0 or a static Admin API access token passed as X-Shopify-Access-Token - Bearer token format is not accepted.

Building an identity graph across Shopify requires stitching together three separate identity surfaces: storefront customers (REST), staff members (GraphQL), and org-level provisioned users (SCIM), each with different scopes, endpoints, and plan requirements.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (for public/custom apps) or Admin API access token (for private apps/custom apps via Partner Dashboard)
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredShopify Plus (with SAML SSO configured)

Authentication

Auth method: OAuth 2.0 (for public/custom apps) or Admin API access token (for private apps/custom apps via Partner Dashboard)

Setup steps

  1. Create an app in the Shopify Partner Dashboard or directly in the store's Admin under 'Apps and sales channels > Develop apps'.
  2. For OAuth 2.0: Register your redirect URI, obtain client_id and client_secret, redirect merchant to https://{store}.myshopify.com/admin/oauth/authorize with required scopes.
  3. Exchange the authorization code for a permanent access token via POST to https://{store}.myshopify.com/admin/oauth/access_token.
  4. Include the token in all requests as the X-Shopify-Access-Token header.
  5. For private/custom apps: generate an Admin API access token directly in the store Admin and use it as X-Shopify-Access-Token.

Required scopes

Scope Description Required for
read_customers Read customer records (storefront-facing users). GET /customers, GET /customers/{id}
write_customers Create, update, and delete customer records. POST /customers, PUT /customers/{id}, DELETE /customers/{id}
read_orders Read order data associated with customers. GET /customers/{id}/orders
read_staff_members Read staff/organization user records (GraphQL only; Shopify Plus org-level). GraphQL query: staffMembers
write_staff_members Create and update staff/organization users (GraphQL only; Shopify Plus org-level). GraphQL mutation: staffMemberCreate, staffMemberUpdate

User object / data model

Field Type Description On create On update Notes
id integer (REST) / gid string (GraphQL) Unique identifier for the customer. auto-generated immutable GraphQL uses global ID format: gid://shopify/Customer/{id}
email string Customer's email address. required (if phone not provided) optional Must be unique per store.
first_name string Customer's first name. optional optional
last_name string Customer's last name. optional optional
phone string Customer's phone number in E.164 format. optional (required if email not provided) optional Must be unique per store.
verified_email boolean Whether the customer's email has been verified. auto-set read-only
state string (enum) Account state: disabled, invited, enabled, declined. auto-set read-only Controlled by invitation/account enable flows, not directly settable.
tags string (comma-separated) Tags attached to the customer. optional optional Max 250 tags; each tag max 255 characters.
note string Internal note about the customer. optional optional
accepts_marketing boolean Whether the customer has opted into marketing emails. optional optional Deprecated in favor of email_marketing_consent.
email_marketing_consent object Marketing consent state, opt-in level, and consent updated timestamp. optional optional Preferred over accepts_marketing.
sms_marketing_consent object SMS marketing consent state and opt-in level. optional optional
addresses array of address objects List of addresses associated with the customer. optional optional Max 3 addresses per customer via REST.
default_address object The customer's default address. auto-set from addresses settable via /customers/{id}/addresses/{address_id}/default
currency string (ISO 4217) Currency the customer used on their last order. auto-set read-only
orders_count integer Number of orders the customer has placed. auto-set read-only
total_spent string (decimal) Total amount spent by the customer across all orders. auto-set read-only
tax_exempt boolean Whether the customer is exempt from taxes. optional optional
metafields array of metafield objects Custom metadata attached to the customer. optional optional Requires separate metafield endpoints or GraphQL.
created_at datetime (ISO 8601) Timestamp when the customer was created. auto-set read-only

Core endpoints

List customers

  • Method: GET
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers.json
  • Watch out for: Cursor-based pagination only; do not use page param. Use Link header rel='next' page_info value.

Request example

GET /admin/api/2024-01/customers.json?limit=50
X-Shopify-Access-Token: {token}

Response example

{
  "customers": [
    {"id": 207119551, "email": "bob@example.com",
     "first_name": "Bob", "last_name": "Doe",
     "state": "enabled"}
  ]
}

Get single customer

  • Method: GET
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers/{customer_id}.json
  • Watch out for: Returns 404 if customer does not exist in the store; IDs are store-scoped.

Request example

GET /admin/api/2024-01/customers/207119551.json
X-Shopify-Access-Token: {token}

Response example

{
  "customer": {
    "id": 207119551,
    "email": "bob@example.com",
    "first_name": "Bob",
    "state": "enabled"
  }
}

Create customer

  • Method: POST
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers.json
  • Watch out for: email or phone is required. Duplicate email returns 422. New customers default to state=disabled until they set a password.

Request example

POST /admin/api/2024-01/customers.json
Content-Type: application/json
{
  "customer": {
    "first_name": "Alice",
    "email": "alice@example.com",
    "verified_email": true
  }
}

Response example

{
  "customer": {
    "id": 1073339461,
    "email": "alice@example.com",
    "first_name": "Alice",
    "state": "disabled"
  }
}

Update customer

  • Method: PUT
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers/{customer_id}.json
  • Watch out for: id must be included in the request body. Partial updates are supported; omitted fields are unchanged.

Request example

PUT /admin/api/2024-01/customers/207119551.json
Content-Type: application/json
{
  "customer": {
    "id": 207119551,
    "note": "VIP customer"
  }
}

Response example

{
  "customer": {
    "id": 207119551,
    "note": "VIP customer",
    "updated_at": "2024-01-15T10:00:00-05:00"
  }
}

Delete customer

  • Method: DELETE
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers/{customer_id}.json
  • Watch out for: Customers with existing orders cannot be deleted; returns 422. Consider anonymizing instead via GraphQL customerDeletePayload.

Request example

DELETE /admin/api/2024-01/customers/207119551.json
X-Shopify-Access-Token: {token}

Response example

{}

Search customers

  • Method: GET
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers/search.json
  • Watch out for: Search uses Shopify's query syntax (field:value). Not all fields are searchable. Does not support cursor pagination; limited to 250 results.

Request example

GET /admin/api/2024-01/customers/search.json?query=email:alice@example.com&limit=10
X-Shopify-Access-Token: {token}

Response example

{
  "customers": [
    {"id": 1073339461, "email": "alice@example.com",
     "first_name": "Alice"}
  ]
}

Send account invite

  • Method: POST
  • URL: https://{store}.myshopify.com/admin/api/2024-01/customers/{customer_id}/send_invite.json
  • Watch out for: Sends a storefront account activation email. Does not apply to staff/admin users. Customer state changes to 'invited'.

Request example

POST /admin/api/2024-01/customers/207119551/send_invite.json
Content-Type: application/json
{"customer_invite": {}}

Response example

{
  "customer_invite": {
    "to": "bob@example.com",
    "from": "store@example.com",
    "subject": "Welcome to Example Store"
  }
}

List staff members (GraphQL)

  • Method: POST
  • URL: https://{store}.myshopify.com/admin/api/2024-01/graphql.json
  • Watch out for: staffMembers query is only available to the store owner token or apps with read_staff_members scope. Organization-level staff management requires Shopify Plus and the Organization Admin API.

Request example

POST /admin/api/2024-01/graphql.json
{
  "query": "{ staffMembers(first: 10) { edges { node { id name email active } } } }"
}

Response example

{
  "data": {
    "staffMembers": {
      "edges": [
        {"node": {"id": "gid://shopify/StaffMember/1",
          "name": "Jane Admin", "email": "jane@store.com",
          "active": true}}
      ]
    }
  }
}

Rate limits, pagination, and events

  • Rate limits: Shopify uses a leaky-bucket algorithm for REST and a calculated-cost system for GraphQL. REST allows 2 requests/second with a bucket of 40 requests. GraphQL uses a point-cost model with 1,000 points/second restore rate and a 50,000-point bucket by default. Shopify Plus stores receive higher limits.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: REST headers: X-Shopify-Shop-Api-Call-Limit (e.g., 32/40). GraphQL headers: X-GraphQL-Cost-Include-Fields, throttleStatus in extensions. Retry-After header returned on 429 responses.
  • Pagination method: cursor
  • Default page size: 50
  • Max page size: 250
  • Pagination pointer: page_info (cursor-based via Link header); limit param controls page size
Plan Limit Concurrent
Basic / Shopify / Advanced REST: 2 req/s (bucket 40); GraphQL: 1,000 points/s restore, 50,000-point bucket 0
Shopify Plus REST: 4 req/s (bucket 80); GraphQL: 2,000 points/s restore, 100,000-point bucket 0
  • Webhooks available: Yes
  • Webhook notes: Shopify supports webhooks for customer lifecycle events. Webhooks can be registered via the Admin API or the Partner Dashboard. Payloads are sent as HTTP POST to your endpoint.
  • Alternative event strategy: Shopify also supports GraphQL webhook subscriptions (webhookSubscriptionCreate mutation) and EventBridge/Pub-Sub delivery for higher reliability.
  • Webhook events: customers/create, customers/update, customers/delete, customers/enable, customers/disable, customer_groups/create, customer_groups/update, customer_groups/delete

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Shopify Plus (with SAML SSO configured)

  • Endpoint: https://{organization}.myshopify.com/scim/v2

  • Supported operations: GET /Users (list users), GET /Users/{id} (get user), POST /Users (provision user), PUT /Users/{id} (replace user), PATCH /Users/{id} (update user), DELETE /Users/{id} (deprovision user), GET /Groups (list groups), POST /Groups (create group), PATCH /Groups/{id} (update group members), DELETE /Groups/{id} (delete group)

Limitations:

  • Requires Shopify Plus plan; not available on Basic, Shopify, or Advanced plans.
  • SAML SSO must be configured before SCIM provisioning can be enabled.
  • SCIM manages organization-level staff users, not storefront customers.
  • Supported IdPs with documented integration: Okta, Microsoft Entra ID (Azure AD), OneLogin.
  • Google Workspace is not officially supported as a SCIM IdP.
  • SCIM token is generated in the Shopify Organization Admin under Security settings.
  • Group support maps to Shopify user groups/permissions; not all IdP group attributes are supported.
  • Deprovisioning via DELETE removes the user from the organization but does not delete their Shopify account.

Common scenarios

Three integration patterns cover the primary use cases. For staff provisioning, SCIM 2.

0 via Okta or Entra ID is the supported path: generate a SCIM token in Organization Admin → Security, configure the SCIM 2. 0 base URL and Bearer token in the IdP, then assign users - Shopify handles POST /Users on assignment and DELETE /Users on deprovisioning.

Note that DELETE removes org access but does not delete the underlying Shopify account. For storefront customer lifecycle management, combine the REST Customer API with webhook subscriptions (customers/create, customers/update, customers/delete) registered via POST /admin/api/2024-01/webhooks.

json; implement idempotency using X-Shopify-Webhook-Id to handle duplicate deliveries.

For GDPR erasure, DELETE /customers/{id} returns 422 for any customer with order history - use the GraphQL customerAnonymize mutation instead, which is irreversible and should be gated behind explicit confirmation logic in your integration.

Provision a new staff user via SCIM (Okta integration)

  1. Ensure Shopify Plus plan is active and SAML SSO is configured in Organization Admin > Security.
  2. Generate a SCIM API token in Shopify Organization Admin > Security > SCIM.
  3. In Okta, configure the Shopify app with SCIM 2.0 base URL: https://{organization}.myshopify.com/scim/v2 and Bearer token authentication.
  4. Assign the user to the Shopify app in Okta; Okta sends POST /Users to the SCIM endpoint.
  5. Shopify creates the staff user in the organization and sends an activation email.
  6. Verify user appears in Shopify Organization Admin > Users.

Watch out for: SCIM token must be regenerated if rotated; update it in Okta immediately to avoid provisioning failures. Deprovisioning removes org access but the Shopify account persists.

Create and tag a storefront customer, then listen for updates via webhook

  1. Register a webhook for customers/create via POST /admin/api/2024-01/webhooks.json with topic and address fields.
  2. POST /admin/api/2024-01/customers.json with email, first_name, last_name, and tags fields.
  3. Shopify creates the customer and fires the customers/create webhook to your endpoint.
  4. Validate the webhook payload using HMAC-SHA256 with X-Shopify-Hmac-Sha256 header.
  5. To update tags later, PUT /admin/api/2024-01/customers/{id}.json with updated tags string.
  6. customers/update webhook fires automatically on any field change.

Watch out for: Webhook delivery is not guaranteed exactly-once; implement idempotency using the X-Shopify-Webhook-Id header to deduplicate.

Anonymize a customer for GDPR compliance

  1. Identify the customer ID from GET /admin/api/2024-01/customers/search.json?query=email:{email}.
  2. Attempt DELETE /admin/api/2024-01/customers/{id}.json; if customer has orders, this returns 422.
  3. For customers with orders, use GraphQL mutation: customerAnonymizePayload via POST to /admin/api/2024-01/graphql.json.
  4. Mutation: mutation { customerAnonymize(customerId: "gid://shopify/Customer/{id}") { anonymizedCustomer { id } userErrors { field message } } }
  5. Verify the customer's PII fields are replaced with anonymized values in the response.

Watch out for: customerAnonymize is irreversible. Anonymized customers cannot be restored. Ensure you have confirmed the erasure request before executing.

Why building this yourself is a trap

Several non-obvious constraints cause integration failures in production. Cursor-based pagination is mandatory for the customers endpoint - the legacy page param is silently ignored or errors; always follow the Link header rel='next' page_info value.

GraphQL uses a point-cost rate limit (50,000-point bucket, 1,000 points/second restore on non-Plus; doubled on Plus) that is separate from and additive to REST limits - deeply nested GraphQL queries can exhaust the bucket faster than expected; inspect the throttleStatus field in the extensions response object.

REST API versions are date-based and supported for approximately one year; apps that do not proactively upgrade will hit deprecation failures. SCIM provisioning operates only at the organization level on Shopify Plus - it cannot be scoped to a single store, and Google Workspace is not an officially supported SCIM IdP.

Finally, the read_staff_members and write_staff_members scopes are only meaningful on Plus org tokens; requesting them on a standard store token returns empty results without an explicit error.

Automate Shopify workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 5, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen