Stitchflow
Paddle logo

Paddle User Management API Guide

API workflow

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

UpdatedMar 11, 2026

Summary and recommendation

Paddle Billing's REST API (base URL: https://api.paddle.com) authenticates via Bearer token using server-side API keys - there is no OAuth 2.0 flow for server-to-server calls. Keys are either default (full access) or restricted (resource-scoped, configured in the dashboard).

A critical distinction: Paddle Classic and Paddle Billing are separate products with incompatible endpoints, auth models, and object schemas; everything here applies to Paddle Billing only. Sandbox and production environments use separate keys and separate base URLs (https://sandbox-api.paddle.com vs https://api.paddle.com) - keys are not interchangeable.

Rate limits are enforced at 100 requests/second per API key; HTTP 429 is returned on breach, and Paddle recommends exponential backoff.

API quick reference

Has user APIYes
Auth methodAPI Key (Bearer token) – Paddle uses server-side API keys passed as Bearer tokens in the Authorization header. No OAuth 2.0 flow is documented for server-to-server calls.
Base URLOfficial docs
SCIM availableNo

Authentication

Auth method: API Key (Bearer token) – Paddle uses server-side API keys passed as Bearer tokens in the Authorization header. No OAuth 2.0 flow is documented for server-to-server calls.

Setup steps

  1. Log in to the Paddle dashboard (sandbox: sandbox-vendors.paddle.com; production: vendors.paddle.com).
  2. Navigate to Developer Tools > Authentication.
  3. Generate an API key (default key or a named key with restricted permissions).
  4. Store the key securely; it is shown only once.
  5. Include the key in every request: Authorization: Bearer {API_KEY}.
  6. For sandbox testing, use base URL https://sandbox-api.paddle.com and a sandbox API key.

Required scopes

Scope Description Required for
No OAuth scopes Paddle API keys are not scope-based in the OAuth sense. Access is controlled by key type: default keys have full access; restricted keys can be limited to specific resources via the dashboard. All API operations

User object / data model

Field Type Description On create On update Notes
id string Unique Paddle-generated customer ID (prefix: ctm_). auto-generated immutable Used as path parameter in all customer endpoints.
email string Customer email address. required optional Must be a valid email format.
name string Full name of the customer. optional optional
locale string BCP 47 locale string (e.g., en-US) used for localized communications. optional optional
marketing_consent boolean Whether the customer has consented to marketing communications. optional optional
status enum Customer status: active or archived. auto-set to active optional (archive via dedicated endpoint) Archived customers cannot transact.
custom_data object Arbitrary key-value pairs for storing custom metadata. optional optional Values must be strings. Max 50 keys.
created_at string (RFC 3339) Timestamp when the customer was created. auto-generated immutable
updated_at string (RFC 3339) Timestamp of the last update. auto-generated auto-updated
import_meta object Metadata about the customer if imported from another system (external_id, imported_from). optional immutable after set Useful for mapping Paddle customers to records in external systems.

Core endpoints

List customers

  • Method: GET
  • URL: https://api.paddle.com/customers
  • Watch out for: Pagination is cursor-based; use meta.pagination.next as the after param. Filtering by email requires exact match.

Request example

GET /customers?per_page=50&status=active
Authorization: Bearer {API_KEY}

Response example

{
  "data": [{"id":"ctm_01h...","email":"user@example.com","name":"Jane Doe","status":"active"}],
  "meta": {"pagination":{"per_page":50,"has_more":true,"next":"ctm_01h..."}}
}

Get customer

  • Method: GET
  • URL: https://api.paddle.com/customers/{customer_id}
  • Watch out for: Returns 404 if the customer_id does not exist in the environment (sandbox vs. production keys are separate).

Request example

GET /customers/ctm_01h...
Authorization: Bearer {API_KEY}

Response example

{
  "data": {"id":"ctm_01h...","email":"user@example.com","name":"Jane Doe","status":"active","custom_data":{}}
}

Create customer

  • Method: POST
  • URL: https://api.paddle.com/customers
  • Watch out for: Duplicate email addresses are allowed; Paddle does not enforce email uniqueness across customers.

Request example

POST /customers
Authorization: Bearer {API_KEY}
Content-Type: application/json

{"email":"user@example.com","name":"Jane Doe","locale":"en-US"}

Response example

{
  "data": {"id":"ctm_01h...","email":"user@example.com","name":"Jane Doe","status":"active","created_at":"2024-01-01T00:00:00Z"}
}

Update customer

  • Method: PATCH
  • URL: https://api.paddle.com/customers/{customer_id}
  • Watch out for: PATCH is partial; only supplied fields are updated. Setting custom_data to {} clears all custom metadata.

Request example

PATCH /customers/ctm_01h...
Authorization: Bearer {API_KEY}
Content-Type: application/json

{"name":"Jane Smith","custom_data":{"plan":"pro"}}

Response example

{
  "data": {"id":"ctm_01h...","email":"user@example.com","name":"Jane Smith","status":"active"}
}

List addresses for customer

  • Method: GET
  • URL: https://api.paddle.com/customers/{customer_id}/addresses
  • Watch out for: Addresses are sub-resources; they must be managed separately from the customer object.

Request example

GET /customers/ctm_01h.../addresses
Authorization: Bearer {API_KEY}

Response example

{
  "data": [{"id":"add_01h...","country_code":"US","postal_code":"10001","status":"active"}]
}

Create address for customer

  • Method: POST
  • URL: https://api.paddle.com/customers/{customer_id}/addresses
  • Watch out for: country_code is required and must be a valid ISO 3166-1 alpha-2 code.

Request example

POST /customers/ctm_01h.../addresses
Authorization: Bearer {API_KEY}
Content-Type: application/json

{"country_code":"US","postal_code":"10001"}

Response example

{
  "data": {"id":"add_01h...","country_code":"US","postal_code":"10001","status":"active"}
}

List subscriptions for customer

  • Method: GET
  • URL: https://api.paddle.com/subscriptions?customer_id={customer_id}
  • Watch out for: Subscriptions are not nested under /customers; filter via query param on the /subscriptions endpoint.

Request example

GET /subscriptions?customer_id=ctm_01h...
Authorization: Bearer {API_KEY}

Response example

{
  "data": [{"id":"sub_01h...","status":"active","customer_id":"ctm_01h...","current_billing_period":{}}]
}

Get customer portal session (magic link)

  • Method: POST
  • URL: https://api.paddle.com/customers/{customer_id}/portal-sessions
  • Watch out for: Portal session URLs are single-use and expire. Do not cache or reuse them.

Request example

POST /customers/ctm_01h.../portal-sessions
Authorization: Bearer {API_KEY}
Content-Type: application/json

{"subscription_ids":["sub_01h..."]}

Response example

{
  "data": {"id":"cpls_01h...","customer_id":"ctm_01h...","urls":{"general":{"overview":"https://customer-portal.paddle.com/..."}},"created_at":"2024-01-01T00:00:00Z"}
}

Rate limits, pagination, and events

  • Rate limits: Paddle enforces rate limits per API key. The documented limit is 100 requests per second per API key for the Paddle Billing API. Exceeding the limit returns HTTP 429.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: HTTP 429 is returned when the limit is exceeded. Paddle recommends exponential backoff. Specific header names (e.g., X-RateLimit-Limit, Retry-After) are referenced in the API overview but exact header names should be confirmed against live responses.
  • Pagination method: cursor
  • Default page size: 50
  • Max page size: 200
  • Pagination pointer: after (cursor-based; use the has_more flag and meta.pagination.next cursor from the response)
Plan Limit Concurrent
All plans (pay-as-you-go) 100 requests/second per API key 0
  • Webhooks available: Yes
  • Webhook notes: Paddle sends signed HTTP POST webhooks to a configured endpoint for all major billing and customer lifecycle events. Webhook payloads are signed with HMAC-SHA256 using a secret key configurable in the dashboard.
  • Alternative event strategy: Polling the /customers and /subscriptions endpoints with updated_at filters can substitute for webhooks in low-volume scenarios.
  • Webhook events: customer.created, customer.updated, subscription.created, subscription.updated, subscription.canceled, subscription.paused, subscription.resumed, subscription.past_due, transaction.created, transaction.updated, transaction.completed, transaction.payment_failed, address.created, address.updated

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Not documented
  • Endpoint: Not documented

Limitations:

  • Paddle does not offer a SCIM 2.0 API. User/customer provisioning must be done via the Paddle REST API directly.
  • No public documentation for SSO or SCIM integration exists as of the policy date.

Common scenarios

The primary integration pattern for identity graph maintenance is mapping Paddle's ctm_-prefixed customer ID to your internal user record at creation time.

POST /customers to create a billing contact, store the returned ctm_ ID against your user record, and use it as the stable join key for all downstream subscription and transaction calls.

Paddle does not enforce email uniqueness - multiple customer records can share the same email address - so deduplication must be owned by the caller before issuing a POST.

For profile sync, use PATCH /customers/{customer_id} with only changed fields; sending custom_data: {} in any PATCH payload will silently clear all existing custom metadata. For subscription lifecycle sync, configure signed webhooks (HMAC-SHA256 via the Paddle-Signature header) and listen for customer.

updated, subscription. created, subscription.

updated, and subscription. canceled; implement idempotency on the event id field to handle Paddle's exponential-backoff retry behavior.

Pagination across /customers and /subscriptions is cursor-based: use meta. pagination.

next as the after parameter, with a default page size of 50 and a maximum of 200.

Provision a new customer on signup

  1. POST /customers with email, name, and any custom_data (e.g., internal user ID).
  2. Store the returned ctm_ ID in your application database mapped to the internal user record.
  3. Optionally POST /customers/{customer_id}/addresses if billing address is collected at signup.
  4. Use the ctm_ ID in subsequent transaction or subscription creation calls.

Watch out for: Paddle does not enforce email uniqueness. Always check your own database for an existing ctm_ ID before creating a new customer to avoid duplicates.

Update customer profile on user account change

  1. Retrieve the stored ctm_ ID for the user from your database.
  2. PATCH /customers/{customer_id} with only the changed fields (name, locale, custom_data, etc.).
  3. Handle 404 gracefully in case the customer was archived or the ID is stale.

Watch out for: Sending custom_data: {} in a PATCH will clear all existing custom metadata. Only include custom_data in the payload if you intend to update it.

Sync customer subscription status to your application

  1. Configure a webhook endpoint in the Paddle dashboard and note the webhook secret.
  2. Listen for customer.updated, subscription.created, subscription.updated, and subscription.canceled events.
  3. Verify the Paddle-Signature header on each inbound webhook using HMAC-SHA256 and your webhook secret.
  4. Extract customer_id and subscription status from the payload and update your application database.
  5. Return HTTP 200 promptly; process asynchronously if needed to avoid timeout-triggered retries.

Watch out for: Paddle retries failed webhook deliveries with exponential backoff. Implement idempotency using the event id field to avoid processing duplicate events.

Why building this yourself is a trap

There is no SCIM 2.0 API and no public SSO documentation for Paddle - provisioning and deprovisioning of dashboard team members has no programmatic path. The Paddle API manages billing contacts (customers), not application-level user accounts; there is no concept of user passwords, sessions, or application roles in the API surface.

This distinction matters for identity graph integrations: ctm_ records represent payers, not authenticated users, and the two must be explicitly joined in your own data layer. custom_data fields accept only string values - nested objects will return a validation error.

Portal session URLs (magic links, generated via POST /customers/{customer_id}/portal-sessions) are single-use and short-lived; they must be generated on demand and never cached.

For teams building on top of Paddle through an MCP server with 60+ deep IT/identity integrations, the absence of SCIM means any identity lifecycle automation must be constructed entirely against the REST API with caller-managed deduplication and no standardized provisioning contract.

Automate Paddle 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 11, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste