Summary and recommendation
Maxio Advanced Billing (the Chargify-lineage API) exposes a REST API at https://{subdomain}.chargify.com authenticated exclusively via HTTP Basic Auth - API key as username, literal string x as password. There are no OAuth 2.0 flows and no scope-based permissions; every API key carries full site-level access.
The rate limit is 10,000 requests per hour per site subdomain, returning HTTP 429 on breach; rate-limit headers are not returned, so exponential backoff must be implemented client-side. Pagination is offset-based using page and per_page params (default 20, max 200 per request, 1-based indexing).
Responses wrap resources in a named key (e.g., {"user": {...}}); SDKs handle unwrapping automatically. A critical architectural distinction governs every integration: Site Users (admin/staff accounts at /users.json) and Customers (billing subscribers at /customers.json) are entirely separate object types with separate endpoints. Provisioning integrations almost always target /customers, not /users.
Maxio has no SCIM 2.0 endpoint; any identity graph synchronization mapping IdP user records to Maxio customer or staff objects requires custom middleware using the reference field as the external ID anchor.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth (API key as username, 'x' as password) |
| Base URL | Official docs |
| SCIM available | No |
Authentication
Auth method: HTTP Basic Auth (API key as username, 'x' as password)
Setup steps
- Log in to your Maxio Advanced Billing (Chargify) site as an admin.
- Navigate to Config > Integrations > API Keys.
- Generate or copy an existing API key.
- Use the API key as the HTTP Basic Auth username and the literal string 'x' as the password in all API requests.
- All requests must use HTTPS; HTTP is not supported.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique identifier for the site user | auto-assigned | immutable | Read-only |
| first_name | string | User's first name | required | optional | |
| last_name | string | User's last name | required | optional | |
| string | User's email address; used as login identifier | required | optional | Must be unique within the site | |
| username | string | Login username for the site user | required | optional | |
| password | string | User's password (write-only) | required | optional | Never returned in responses |
| created_at | datetime (ISO 8601) | Timestamp when the user was created | auto-assigned | immutable | Read-only |
| updated_at | datetime (ISO 8601) | Timestamp of last update | auto-assigned | auto-updated | Read-only |
Core endpoints
List Users
- Method: GET
- URL:
https://{subdomain}.chargify.com/users.json - Watch out for: Returns site-level admin/staff users only, not end-customer (subscriber) records.
Request example
GET /users.json?page=1&per_page=20 HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Response example
[
{
"user": {
"id": 123,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"username": "janedoe"
}
}
]
Read User
- Method: GET
- URL:
https://{subdomain}.chargify.com/users/{id}.json - Watch out for: Returns 404 if the user ID does not exist in the site.
Request example
GET /users/123.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Response example
{
"user": {
"id": 123,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"username": "janedoe"
}
}
Create User
- Method: POST
- URL:
https://{subdomain}.chargify.com/users.json - Watch out for: Creates a site admin/staff user. Does not create a billing customer (use /customers.json for that).
Request example
POST /users.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Content-Type: application/json
{"user":{"first_name":"Jane","last_name":"Doe","email":"jane@example.com","username":"janedoe","password":"secret"}}
Response example
{
"user": {
"id": 124,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"username": "janedoe"
}
}
Update User
- Method: PUT
- URL:
https://{subdomain}.chargify.com/users/{id}.json - Watch out for: Only fields included in the request body are updated; omitted fields retain existing values.
Request example
PUT /users/123.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Content-Type: application/json
{"user":{"first_name":"Janet"}}
Response example
{
"user": {
"id": 123,
"first_name": "Janet",
"last_name": "Doe",
"email": "jane@example.com"
}
}
Delete User
- Method: DELETE
- URL:
https://{subdomain}.chargify.com/users/{id}.json - Watch out for: Deletion is permanent and cannot be undone via the API.
Request example
DELETE /users/123.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Response example
HTTP/1.1 204 No Content
List Customers
- Method: GET
- URL:
https://{subdomain}.chargify.com/customers.json - Watch out for: Customers are billing entities (subscribers), distinct from site Users (admin accounts). Most provisioning integrations target /customers, not /users.
Request example
GET /customers.json?page=1&per_page=50 HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Response example
[
{
"customer": {
"id": 5001,
"first_name": "Bob",
"last_name": "Smith",
"email": "bob@acme.com",
"reference": "acme-001"
}
}
]
Create Customer
- Method: POST
- URL:
https://{subdomain}.chargify.com/customers.json - Watch out for: The 'reference' field is your external ID; it must be unique per site. Duplicate reference values return a 422 error.
Request example
POST /customers.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Content-Type: application/json
{"customer":{"first_name":"Bob","last_name":"Smith","email":"bob@acme.com","reference":"acme-001"}}
Response example
{
"customer": {
"id": 5002,
"first_name": "Bob",
"last_name": "Smith",
"email": "bob@acme.com",
"reference": "acme-001"
}
}
Update Customer
- Method: PUT
- URL:
https://{subdomain}.chargify.com/customers/{id}.json - Watch out for: Changing email does not trigger any re-verification flow via the API.
Request example
PUT /customers/5001.json HTTP/1.1
Host: {subdomain}.chargify.com
Authorization: Basic {base64(api_key:x)}
Content-Type: application/json
{"customer":{"email":"bob.new@acme.com"}}
Response example
{
"customer": {
"id": 5001,
"email": "bob.new@acme.com"
}
}
Rate limits, pagination, and events
- Rate limits: Maxio Advanced Billing enforces per-site rate limits. The default limit is 10,000 API calls per hour per site. Exceeding the limit returns HTTP 429.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: Rate limit headers are not documented as returned by the API. Retry after receiving a 429 response; official docs recommend exponential backoff. Limits apply per site subdomain.
- Pagination method: offset
- Default page size: 20
- Max page size: 200
- Pagination pointer: page / per_page
| Plan | Limit | Concurrent |
|---|---|---|
| All plans (default) | 10,000 requests/hour per site | 0 |
- Webhooks available: Yes
- Webhook notes: Maxio Advanced Billing supports webhooks for subscription and customer lifecycle events. Webhooks are configured per site in the dashboard under Config > Integrations > Webhooks.
- Alternative event strategy: Poll /customers.json or /subscriptions.json with updated_at filters for change detection if webhooks are not feasible.
- Webhook events: customer_update, signup_success, signup_failure, subscription_state_change, subscription_product_change, renewal_success, renewal_failure, payment_success, payment_failure, expiring_card, billing_date_change, subscription_deletion
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Not documented
- Endpoint: Not documented
Limitations:
- Maxio does not offer a SCIM 2.0 endpoint for automated user provisioning/deprovisioning.
- SSO via SAML is available (Okta, Entra ID documented) but SCIM sync is not supported.
- User lifecycle management must be performed via the REST API or manually in the dashboard.
Common scenarios
Three scenarios cover the primary provisioning lifecycle. To provision a new billing customer, POST to /customers.
json with first_name, last_name, email, and a unique reference (your external or IdP user ID). Capture the returned customer.
id, then POST to /subscriptions. json with customer_id and product_handle to activate.
Always check GET /customers/lookup. json?
reference={ref} before creating - duplicate reference values return a 422 with no upsert behavior. To deprovision, resolve the customer via the reference lookup, list active subscriptions via GET /customers/{id}/subscriptions.
json, then cancel each via POST /subscriptions/{id}/cancel. json.
Cancellation is preferred over DELETE because it allows reactivation; deletion is permanent and irreversible via the API. Customer-record deletion is not available via a standard API endpoint and must be handled in the dashboard.
To sync an email change from an IdP, resolve the Maxio customer ID via the reference lookup, then PUT /customers/{id}. json with {"customer": {"email": "new@example.
com"}}. Email changes do not trigger re-verification; log the updated_at timestamp from the 200 response for audit trail purposes.
Provision a new billing customer and assign a subscription
- POST /customers.json with first_name, last_name, email, and a unique reference (your external user ID).
- Capture the returned customer.id.
- POST /subscriptions.json with customer_id, product_handle, and payment_profile attributes to activate the subscription.
- Store the returned subscription.id for future lifecycle management.
Watch out for: If the reference already exists, the API returns 422. Check for existing customers via GET /customers/lookup.json?reference={ref} before creating.
Deprovision a user (cancel subscription and archive customer)
- GET /customers/lookup.json?reference={external_id} to resolve the Maxio customer ID.
- GET /customers/{id}/subscriptions.json to list active subscriptions.
- DELETE /subscriptions/{subscription_id}.json (or POST /subscriptions/{id}/cancel.json) for each active subscription.
- Optionally archive the customer record via the dashboard; there is no dedicated customer-delete API endpoint in standard plans.
Watch out for: Deleting a subscription is irreversible via the API. Use cancellation (which allows reactivation) unless permanent removal is intended.
Sync updated email address from IdP to Maxio customer record
- Receive user update event from your IdP (e.g., Okta webhook or SCIM update to your middleware).
- GET /customers/lookup.json?reference={external_id} to find the Maxio customer ID.
- PUT /customers/{id}.json with {"customer": {"email": "new@example.com"}} to update the email.
- Verify the 200 response and log the updated_at timestamp.
Watch out for: Maxio has no SCIM endpoint; this sync must be implemented in custom middleware. Email changes do not trigger re-verification.
Why building this yourself is a trap
The absence of SCIM is the primary integration trap: there is no standards-based endpoint for automated provisioning, so any identity graph that needs to reflect Maxio staff or customer state must maintain its own sync layer.
The API key model compounds this - keys are site-scoped and grant full access with no granular scopes, meaning a compromised or orphaned key exposes the entire site. Multi-site organizations face a multiplier effect: each site has its own subdomain, its own API keys, and its own user list, with no cross-site API surface.
Webhook payloads default to form-encoded POST bodies rather than JSON, which surprises integrators expecting standard JSON delivery. The Maxio entity also covers two distinct API surfaces - Advanced Billing (Chargify lineage) and SaaS Metrics (SaaSOptics lineage) - which have separate APIs; assuming a single unified API is a common and costly architectural mistake.
An integration platform offering an MCP server with 60+ deep IT/identity integrations can abstract per-site key management, normalize the Users/Customers object split, and maintain the identity graph sync that Maxio's native API does not provide.
Automate Maxio 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.