Stitchflow
Stripe logo

Stripe User Management API Guide

API workflow

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

UpdatedMar 9, 2026

Summary and recommendation

Stripe exposes two entirely separate user concepts that must not be conflated in an identity graph: Customers (`/v1/customers`) are payment entities managed via the standard REST API; Dashboard Team Members are internal users managed via the SCIM 2.0 endpoint (`https://dashboard.stripe.com/scim/v2`), which requires an enterprise agreement and active SSO.

Mixing these two systems in a provisioning pipeline is a common and consequential architectural error.

Authentication uses HTTP Basic Auth with a secret API key (key as username, empty password) or a Bearer token via the Authorization header. For least-privilege integrations, use Restricted Keys scoped to specific resources (e.g., `customers:read`, `customers:write`) rather than a full secret key.

Live mode enforces 100 read req/s and 100 write req/s per key; HTTP 429 responses require exponential backoff. Rate limit state is surfaced via `Stripe-Ratelimit-Limit` and `Stripe-Ratelimit-Remaining` response headers.

API quick reference

Has user APIYes
Auth methodHTTP Basic Auth with secret API key (key as username, empty password); Bearer token also accepted via Authorization header
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredEnterprise (requires SSO enabled via enterprise agreement; private preview as of last known documentation)

Authentication

Auth method: HTTP Basic Auth with secret API key (key as username, empty password); Bearer token also accepted via Authorization header

Setup steps

  1. Log in to the Stripe Dashboard and navigate to Developers → API keys.
  2. Copy the Secret key (sk_live_... or sk_test_... for test mode).
  3. Pass the key as the username in HTTP Basic Auth: Authorization: Basic base64(sk_live_xxx:) or as Authorization: Bearer sk_live_xxx.
  4. For restricted access, create a Restricted Key with only the required permissions scoped to specific resources.
  5. For OAuth-connected platforms, obtain an access token via Stripe Connect OAuth 2.0 flow (authorize endpoint: https://connect.stripe.com/oauth/authorize).

Required scopes

Scope Description Required for
read_write (full secret key) Full read/write access to all API resources on the account. Creating, updating, and deleting customers and other resources.
Restricted Key: customers:read Read-only access to Customer objects. Listing and retrieving customers.
Restricted Key: customers:write Write access to Customer objects. Creating and updating customers.

User object / data model

Field Type Description On create On update Notes
id string Unique identifier for the Customer object (cus_...). auto-generated immutable Used as cursor for pagination.
email string Customer's email address. optional updatable Not required to be unique.
name string Customer's full name. optional updatable
phone string Customer's phone number. optional updatable
description string Arbitrary description for internal reference. optional updatable
metadata object Key-value pairs for storing additional structured data. optional updatable Max 50 keys; key max 40 chars; value max 500 chars.
address object Customer's billing address (line1, line2, city, state, postal_code, country). optional updatable
shipping object Shipping address and contact info. optional updatable
balance integer Current credit/debit balance in smallest currency unit. optional updatable Negative = credit; positive = amount owed.
currency string Three-letter ISO currency code for the customer's default currency. optional updatable Set on first invoice or explicitly.
default_source string ID of the default payment source attached to the customer. optional updatable Deprecated in favor of default_payment_method on subscriptions.
invoice_prefix string Prefix for invoice numbers generated for this customer. optional updatable
invoice_settings object Default invoice settings including default_payment_method and footer. optional updatable
tax_exempt string Tax exemption status: none, exempt, or reverse. optional updatable
created timestamp Unix timestamp of when the customer was created. auto-generated immutable
livemode boolean Whether the object exists in live mode or test mode. auto-set immutable
deleted boolean Present and true if the customer has been deleted. n/a set on delete Deleted customers cannot be recovered via API.

Core endpoints

Create Customer

  • Method: POST
  • URL: https://api.stripe.com/v1/customers
  • Watch out for: Email uniqueness is NOT enforced; duplicate emails create separate customer objects.

Request example

curl https://api.stripe.com/v1/customers \
  -u sk_test_xxx: \
  -d email='user@example.com' \
  -d name='Jane Doe' \
  -d metadata[internal_id]='usr_001'

Response example

{
  "id": "cus_ABC123",
  "object": "customer",
  "email": "user@example.com",
  "name": "Jane Doe",
  "created": 1700000000,
  "livemode": false
}

Retrieve Customer

  • Method: GET
  • URL: https://api.stripe.com/v1/customers/{id}
  • Watch out for: Retrieving a deleted customer returns the object with deleted: true rather than a 404.

Request example

curl https://api.stripe.com/v1/customers/cus_ABC123 \
  -u sk_test_xxx:

Response example

{
  "id": "cus_ABC123",
  "object": "customer",
  "email": "user@example.com",
  "name": "Jane Doe",
  "deleted": false
}

Update Customer

  • Method: POST
  • URL: https://api.stripe.com/v1/customers/{id}
  • Watch out for: Stripe uses POST (not PATCH) for updates. Passing metadata='' clears all metadata keys.

Request example

curl https://api.stripe.com/v1/customers/cus_ABC123 \
  -u sk_test_xxx: \
  -d email='new@example.com' \
  -d 'metadata[status]=active'

Response example

{
  "id": "cus_ABC123",
  "email": "new@example.com",
  "metadata": {"status": "active"}
}

Delete Customer

  • Method: DELETE
  • URL: https://api.stripe.com/v1/customers/{id}
  • Watch out for: Deletion is permanent and irreversible. Active subscriptions are cancelled immediately.

Request example

curl -X DELETE https://api.stripe.com/v1/customers/cus_ABC123 \
  -u sk_test_xxx:

Response example

{
  "id": "cus_ABC123",
  "object": "customer",
  "deleted": true
}

List Customers

  • Method: GET
  • URL: https://api.stripe.com/v1/customers
  • Watch out for: Filter by email using ?email=user@example.com but this is an exact match and case-sensitive.

Request example

curl 'https://api.stripe.com/v1/customers?limit=10&starting_after=cus_XYZ' \
  -u sk_test_xxx:

Response example

{
  "object": "list",
  "data": [{"id": "cus_ABC123", ...}],
  "has_more": true,
  "url": "/v1/customers"
}

Search Customers

  • Method: GET
  • URL: https://api.stripe.com/v1/customers/search
  • Watch out for: Search uses a different pagination model (next_page token, not cursor IDs). Indexed with a delay; not real-time.

Request example

curl 'https://api.stripe.com/v1/customers/search?query=email%3A%27user%40example.com%27' \
  -u sk_test_xxx:

Response example

{
  "object": "search_result",
  "data": [{"id": "cus_ABC123", ...}],
  "has_more": false,
  "next_page": null
}

Create Portal Session (self-service)

  • Method: POST
  • URL: https://api.stripe.com/v1/billing_portal/sessions
  • Watch out for: Portal configuration must be created in the Dashboard first. Session URLs expire after use or after a short TTL.

Request example

curl https://api.stripe.com/v1/billing_portal/sessions \
  -u sk_test_xxx: \
  -d customer=cus_ABC123 \
  -d return_url='https://example.com/account'

Response example

{
  "id": "bps_xxx",
  "object": "billing_portal.session",
  "url": "https://billing.stripe.com/session/xxx"
}

Retrieve Dashboard User (Team Member) - via SCIM

  • Method: GET
  • URL: https://dashboard.stripe.com/scim/v2/Users/{id}
  • Watch out for: SCIM endpoint is separate from the REST API. Requires SSO enabled and enterprise agreement. Roles are managed via SAML attribute statements, not SCIM.

Request example

curl https://dashboard.stripe.com/scim/v2/Users/usr_xxx \
  -H 'Authorization: Bearer <scim_token>'

Response example

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "usr_xxx",
  "userName": "user@example.com",
  "active": true
}

Rate limits, pagination, and events

  • Rate limits: Stripe enforces rate limits per secret API key. Live mode allows 100 read requests/second and 100 write requests/second by default. Test mode limits are lower. Limits can be raised by contacting Stripe support.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: HTTP 429 is returned when rate limit is exceeded. Stripe recommends exponential backoff. Headers Stripe-Ratelimit-Limit and Stripe-Ratelimit-Remaining are returned on responses.
  • Pagination method: cursor
  • Default page size: 10
  • Max page size: 100
  • Pagination pointer: starting_after / ending_before (cursor-based using object ID); limit controls page size
Plan Limit Concurrent
Standard (live mode) 100 read req/s, 100 write req/s 0
Standard (test mode) 25 read req/s, 25 write req/s 0
  • Webhooks available: Yes
  • Webhook notes: Stripe sends webhook events to a configured HTTPS endpoint when objects change. Configure endpoints in Dashboard → Developers → Webhooks or via the Webhook Endpoints API.
  • Alternative event strategy: Stripe provides a CLI (stripe listen) for local webhook testing. Polling the List Customers endpoint with created[gte] filter is an alternative for batch sync.
  • Webhook events: customer.created, customer.updated, customer.deleted, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.source.created, customer.source.deleted, billing_portal.session.created

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Enterprise (requires SSO enabled via enterprise agreement; private preview as of last known documentation)

  • Endpoint: https://dashboard.stripe.com/scim/v2

  • Supported operations: GET /Users (list team members), GET /Users/{id} (retrieve team member), POST /Users (provision team member), PUT /Users/{id} (update team member), PATCH /Users/{id} (partial update / deactivate), DELETE /Users/{id} (deprovision team member)

Limitations:

  • SCIM manages Stripe Dashboard team members only, not payment Customers.
  • Role assignment is controlled via SAML attribute statements (e.g., from Okta/Entra), not SCIM attributes.
  • Requires SSO to be configured and active before SCIM can be enabled.
  • SCIM provisioning was in private preview; general availability status should be confirmed with Stripe.
  • Only Okta and Microsoft Entra ID (Azure AD) are documented as supported IdPs for SCIM.
  • Google Workspace and OneLogin are not documented as supported for SCIM.

Common scenarios

Three scenarios cover the primary integration patterns. First, provisioning a paying customer: POST to /v1/customers with email, name, and metadata, then attach a PaymentMethod via /v1/payment_methods/{pm_id}/attach - attachment does not automatically set the method as default.

a follow-up POST to /v1/customers/{id} with invoice_settings[default_payment_method] is required. Never pass raw card numbers server-side; tokenize via Stripe.

js.

Second, syncing Dashboard team members via SCIM (Okta): configure the SCIM base URL (https://dashboard.stripe.com/scim/v2) and bearer token in Okta, then set Stripe roles exclusively via SAML attribute statements - SCIM attributes cannot control role assignment. Deprovisioning sends PATCH /scim/v2/Users/{id} with active=false; full record deletion behavior should be verified against your enterprise agreement terms.

Third, bulk customer export: paginate GET /v1/customers?limit=100 using starting_after cursor (last object ID) until has_more=false. The Search endpoint (/v1/customers/search) uses a separate next_page token pagination model and has indexing latency - it is not suitable for real-time lookups of recently created records.

Provision a new paying customer and attach a payment method

  1. POST /v1/customers with email, name, and metadata fields to create the Customer object.
  2. Use Stripe.js / Payment Element on the frontend to collect card details and create a PaymentMethod (pm_...).
  3. POST /v1/payment_methods/{pm_id}/attach with customer=cus_... to attach the method.
  4. POST /v1/customers/{id} with invoice_settings[default_payment_method]=pm_... to set as default.
  5. Listen for customer.created and customer.updated webhooks to sync to your database.

Watch out for: Never pass raw card numbers to your server; always tokenize via Stripe.js. Attaching a PaymentMethod does not automatically set it as default.

Sync Stripe Dashboard team members via SCIM (Okta)

  1. Confirm enterprise agreement with Stripe and enable SSO (SAML 2.0) via Dashboard → Settings → Team and security.
  2. In Okta, add the Stripe SCIM application and configure the SCIM base URL: https://dashboard.stripe.com/scim/v2.
  3. Generate a SCIM bearer token in the Stripe Dashboard and enter it in Okta as the API token.
  4. Configure SAML attribute statements in Okta to pass the desired Stripe role (e.g., developer, analyst) as a SAML attribute.
  5. Assign users in Okta; Okta will POST /scim/v2/Users to provision them in Stripe Dashboard.
  6. Deprovisioning in Okta sends PATCH /scim/v2/Users/{id} with active=false to deactivate the team member.

Watch out for: Roles cannot be set via SCIM attributes; they must be configured via SAML attribute statements. Removing a user from Okta deactivates but may not fully delete the Stripe team member record.

Bulk export and reconcile all customers

  1. GET /v1/customers?limit=100 to fetch the first page.
  2. Use the last object's id as starting_after in the next request; repeat until has_more=false.
  3. Alternatively, use GET /v1/customers/search with a query filter (e.g., metadata['status']:'active') for filtered exports.
  4. Store the customer id and updated timestamp; use customer.updated webhook events to keep the local copy current.

Watch out for: The list endpoint returns up to 100 objects per page maximum. Search results have indexing lag; do not rely on search for immediately-created records. Large exports may hit rate limits; implement exponential backoff on HTTP 429.

Why building this yourself is a trap

The most operationally dangerous assumption is that removing a Dashboard team member revokes their API access. It does not. API keys issued under a Developer-role account remain valid until explicitly rotated or revoked under Developers → API keys - this is a separate, manual action with no automated trigger on team member removal.

A second structural trap relevant to any identity graph: Customer email is not a unique key. POST /v1/customers with a duplicate email creates a new, distinct Customer object rather than returning or updating the existing one.

Any identity graph that uses email as a deduplication key against Stripe Customer IDs will produce fan-out without explicit pre-flight lookup via GET /v1/customers?email=. The DELETE /v1/customers/{id} operation is permanent and immediately cancels all active subscriptions - there is no soft-delete or recovery path.

For platforms using Stripe Connect, Connected Accounts are a third user concept managed via /v1/accounts, entirely separate from both Customers and SCIM-managed Team Members. An identity graph that does not model all three object types distinctly will produce incorrect access state.

SCIM availability remains in private preview as of the last documented state; confirm general availability directly with Stripe before building a production provisioning dependency on it.

Automate Stripe 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 9, 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