Customers
Manage customer lifecycle, professional profiles, and organizational associations.
Customers
The Customer API is the central hub for managing your client base. It allows you to synchronize your internal CRM data, manage billing profiles, and monitor subscription health across your organization.
List All Customers
Retrieve a paginated collection of customers associated with your organization. This endpoint is ideal for dashboard overviews and batch synchronization processes.
customers:readApiAccessPermission::CUSTOMERS_READQuery Parameters
sizeintegerpageintegerResponses
A paginated list of customers.
{
"entities": "Customer",
"count": 150,
"per_page": 100,
"pages": {
"current": 1,
"max": 2
},
"elements": [
{
"id": "67248941cbe2b1ff8b099a6c",
"external_id": "CRM-UID-9921",
"company_name": "Global Logistics S.A.",
"email": "billing@globallogistics.com"
}
]
}Technical Implementation
curl --location --request GET \
'https://api.hub.donutwork.com/2026-02-01/customers.json?size=20' \
--header 'Authorization: Bearer YOUR_API_KEY'const customers = await sdk.customers.list({ size: 20 });
console.table(customers.elements);Create a New Customer
Initialize a new customer profile. This is the prerequisite step for attaching subscriptions or processing one-time charges. High-quality data at this stage ensures accurate tax calculation and communication delivery.
customers:writeApiAccessPermission::CUSTOMERS_WRITEQuery Parameters
No query parameters required.
Request Body
{
"customer": {
"company_name": "Acme Corporation",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@acme.com",
"externalId": "CRM-UID-9921",
"address": {
"country": "US",
"city": "San Francisco",
"address": "123 Market St"
}
}
}customer.company_namestringRequiredcustomer.emailstringRequiredcustomer.first_namestringRequiredcustomer.last_namestringRequiredcustomer.address.countrystringRequiredcustomer.externalIdstringcustomer.address.citystringcustomer.address.addressstringcustomer.address.postal_codestringcustomer.address.statestringcustomer.address.vat_numberstringcustomer.propertiesarraycustomer.partner.idstringResponses
Customer profile successfully created.
{
"customer": {
"id": "67c9...",
"partner": null
}
}A profile with this email or externalId already exists.
{
"error": "A customer already exists with this email"
}Retrieve Customer Details
Fetch the complete profile for a specific customer. This includes organizational metadata, contact information, and summarized subscription status.
customers:readApiAccessPermission::CUSTOMERS_READQuery Parameters
customerIdstringRequiredResponses
Complete customer profile retrieved.
{
"id": "672489...",
"email": "jane.doe@acme.com",
"company_name": "Acme Corporation",
"externalId": "CRM-UID-9921"
}Search by External Identifier
Locate a customer profile using your internal system's identifier (externalId). This is essential for maintaining synchronization between Donutwork and your primary CRM/ERP.
customers:readApiAccessPermission::CUSTOMERS_READQuery Parameters
externalIdstringRequiredResponses
Customer profile found.
{
"id": "672489...",
"email": "jane.doe@acme.com",
"externalId": "CRM-UID-9921"
}No customer matches the provided externalId.
{
"error": "This customer does not exist"
}Update Customer Profile
Modify an existing customer record. This endpoint supports partial updates (PATCH-style behavior); only the fields included in the request body will be modified. This is useful for updating contact information or adding organizational tags.
customers:writeApiAccessPermission::CUSTOMERS_WRITEQuery Parameters
customerIdstringRequiredRequest Body
{
"customer": {
"email": "updated.contact@acme.com",
"tags": [
"enterprise",
"priority-support"
]
}
}customer.emailstringcustomer.first_namestringcustomer.last_namestringcustomer.company_namestringcustomer.tagsarraycustomer.address.countrystringResponses
Customer profile successfully updated.
{
"customer": {
"id": "6724..."
}
}Decommission Customer Profile
Permanently remove a customer profile and all associated metadata. This action is destructive and irreversible. Ensure that all active subscriptions are terminated before choosing to decommission a profile.
customers:writeApiAccessPermission::CUSTOMERS_WRITEQuery Parameters
customerIdstringRequiredResponses
Deletion request accepted and queued for processing.
{
"id": "67c9...",
"deleted": true
}Customer Properties (Metadata)
Extend the customer profile with custom key-value pairs. Properties are ideal for storing domain-specific data, integration identifiers, or specialized configuration flags that are not captured by standard profile fields.
Retrieve All Properties
Fetch the complete set of custom metadata associated with a customer.
customer_properties:readApiAccessPermission::CUSTOMER_PROPERTIES_READQuery Parameters
customerIdstringRequiredResponses
Collection of customer-specific properties retrieved.
{
"count": 2,
"type": "CustomerProperties",
"elements": {
"crm_segment": "enterprise",
"onboarding_status": "completed"
}
}Atomic Update/Insert (Upsert) Properties
Update existing property values or append new keys to the customer's property collection. This endpoint performs an "upsert" operation; keys that do not exist will be created, and existing keys will have their values overwritten.
customer_properties:writeApiAccessPermission::CUSTOMER_PROPERTIES_WRITEQuery Parameters
customerIdstringRequiredRequest Body
{
"CustomerProperties": {
"support_tier": "platinum",
"preferred_language": "en"
}
}CustomerPropertiesobjectRequiredResponses
Properties successfully updated.
{
"customer": {
"id": "6724..."
},
"CustomerProperties": {
"support_tier": "platinum",
"preferred_language": "en"
}
}Secure Hosted Payment Link
Generate a unique, short-lived URL for the Donutwork Hosted Payment Page. This secure interface allow customers to update their sensitive payment information (e.g., Credit Card, SEPA Mandate) without sensitive data passing through your servers.
customers:readApiAccessPermission::CUSTOMERS_READQuery Parameters
customerIdstringRequiredResponses
Secure payment link generated.
{
"url": "https://pay.donutwork.com/l/xxxxxxxxxx"
}Subscription Management
Manage recurring billing cycles and plan associations for your customers. Subscriptions automate the generation of invoices and payment processing based on predefined logic.
List Active and Historical Subscriptions
Retrieve a collection of all subscription entities associated with a customer, including active, canceled, and past-due states.
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
customerIdstringRequiredResponses
A list of customer subscriptions.
{
"type": "CustomerSubscriptions",
"elements": [
{
"id": "sub_123",
"name": "Enterprise SaaS Plan",
"status": "success",
"next_renew": "2026-04-01",
"price": {
"amount": 499,
"vat": {
"percentage": 22,
"name": "IVA"
}
}
}
]
}Attach New Subscription
Provision a new subscription for a customer. This endpoint allows for runtime overrides of plan defaults, such as custom discounts and initial addon quantities.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredRequest Body
{
"subscription": {
"id": "PLAN_PREMIUM_V2",
"taxes": "TAX_STANDARD_22",
"global_discount": {
"value": 15,
"type": "percentage"
},
"addons": [
{
"element": "api_quota",
"quantity": 10000
}
]
}
}subscription.idstringRequiredsubscription.taxesstringRequiredsubscription.global_discount.valuenumbersubscription.global_discount.typestringsubscription.addonsarrayResponses
Subscription successfully provisioned.
{
"id": "sub_67c9...",
"status": "active"
}Detach Subscription
Detach a subscription from the customer profile.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
Subscription detached from customer profile.
{
"id": "sub_67c9...",
"deleted": true
}Retrieve Subscription Status
Read the operational status of a specific customer subscription.
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
Current status for the selected subscription.
{
"id": "sub_123",
"status": "active",
"next_renew": "2026-05-01"
}Subscription does not exist for the specified customer.
{
"error": "This subscription does not exists for this customer"
}Modify Subscription Status
Manually transition a subscription between operational states. This is typically used for administrative overrides or manual churn management.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredRequest Body
{
"subscription": {
"status": "dismiss"
}
}subscription.statusstringRequiredResponses
Status successfully updated.
{
"id": "sub_123",
"status": "dismiss",
"next_renew": null
}Advanced Subscription Lifecycle (Additive)
These endpoints extend subscription lifecycle management without replacing existing legacy routes. Use them when you need pause/resume, plan amendments, scheduled changes, and proration-aware updates.
Read Extended Lifecycle State
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
Extended lifecycle metadata for the subscription.
{
"id": "sub_123",
"status": "success",
"lifecycle_status": "active",
"cancel_at_period_end": false,
"scheduled_change": null,
"carryover_credit": 0
}Pause, Resume, Cancel-at-Period-End
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredRequest Body
{
"lifecycle": {
"idempotency_key": "lifecycle-evt-001"
}
}lifecycle.idempotency_keystringResponses
Lifecycle transition applied.
{
"id": "sub_123",
"lifecycle_status": "paused",
"amendment": {
"action": "pause",
"status": "applied"
}
}Use the same payload model with:
.../lifecycle/resume.json.../lifecycle/cancel-at-period-end.json.../lifecycle/undo-cancel-at-period-end.json
Pause/resume billing behavior:
- No retroactive automatic billing is created when resuming after a long pause.
- If resumed two months later (for example pause on May 1, 2026 and resume on July 1, 2026), renewal is re-anchored to the resume date (or future
resume_atif provided).
Undo-cancel behavior:
- If a subscription is
cancel_pending, you can revert it toactivebefore renewal boundary. - Plan/addons remain the current ones on the subscription; cancellation flag is removed.
Change Plan or Addons with Immediate/Scheduled Application
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
No query parameters required.
Request Body
{
"lifecycle": {
"plan_id": "PLAN_ENTERPRISE_V3",
"taxes": "TAX_STANDARD_22",
"when": "period_end",
"proration": true
}
}lifecycle.plan_idstringRequiredlifecycle.taxesstringlifecycle.whenstringlifecycle.prorationbooleanResponses
Plan change accepted (immediate or scheduled).
{
"id": "sub_123",
"scheduled_change": {
"type": "plan",
"apply_on": "2026-06-01"
},
"amendment": {
"action": "change_plan",
"status": "applied"
}
}customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
No query parameters required.
Request Body
{
"lifecycle": {
"addons": [
{
"element": "workspace_seat",
"quantity": 25
}
],
"when": "immediate",
"proration": true
}
}Responses
Addon amendment applied with optional proration.
{
"id": "sub_123",
"carryover_credit": 18.5,
"amendment": {
"action": "change_addons",
"status": "applied"
}
}Proration Preview (No Mutation)
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
No query parameters required.
Request Body
{
"lifecycle": {
"action": "change_addons",
"addons": [
{
"element": "workspace_seat",
"quantity": 40
}
]
}
}Responses
Economic preview of the amendment without persisting changes.
{
"action": "change_addons",
"proration": {
"old_due": 499,
"new_due": 679,
"delta": 180,
"direction": "debit"
}
}Amendment History
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
sizeintegerpageintegerResponses
Paginated amendment stream for audit and support operations.
{
"type": "CustomerSubscriptionAmendments",
"elements": [
{
"action": "change_addons",
"when": "immediate",
"status": "applied"
}
]
}Backward Compatibility
Lifecycle endpoints are additive and opt-in. Existing integrations can keep using current routes unchanged.
| Area | Legacy Contract | Lifecycle Extension |
|---|---|---|
| Attach/Detach | Unchanged | Still valid |
| Status route | Unchanged payload keys | Additional lifecycle visibility |
| Addons route | Unchanged | Optional proration-aware lifecycle change-addons |
| Usage/metering | Unchanged | Unchanged |
| Existing panel/API flows | Unchanged | New actions available when explicitly called |
Subscription Addons
Manage the quantity of modular features (addons) attached to a specific subscription. Addons allow for flexible, feature-based pricing.
Audit Addon Quantities
Retrieve a list of all addons currently active on a subscription along with their provisioned quantities.
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
List of provisioned addons.
[
{
"element": "compute_units",
"quantity": 10
},
{
"element": "secondary_domains",
"quantity": 2
}
]Reconcile Addon Quantities
Update the provisioned quantities for one or more addons. This operation is additive or disruptive based on the provided quantity values.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredRequest Body
{
"addons": [
{
"element": "compute_units",
"quantity": 15
}
]
}addonsarrayRequiredaddons[].elementstringRequiredaddons[].quantityintegerRequiredResponses
Addon quantities successfully updated.
{
"status": "updated"
}Simulation Interface (Sandbox Only)
Accelerate development cycles by simulating subscription lifecycle events in non-production environments.
Simulate Success Event
Force a successful renewal or provisioning event for a subscription.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
{
"status": "simulated_success"
}Simulate Failure Event
Force a failed renewal or payment rejection event.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
{
"status": "simulated_failure"
}Metered Usage Tracking
For subscriptions with metering: true, consumption must be reported via these endpoints. Usage is aggregated and billed at the end of the current billing cycle.
Report Usage Event
Record a new consumption event (e.g., API requests, data processed, seats occupied). Each record requires a unique externalId to prevent idempotent replay errors.
customer_subscriptions:writeApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_WRITEQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredRequest Body
{
"usage": {
"amount": 10.5,
"value": 100,
"value_type": "api_requests",
"externalId": "evt_unique_uuid_123"
}
}usage.amountnumberRequiredusage.valuenumberRequiredusage.value_typestringRequiredusage.externalIdstringRequiredResponses
Usage successfully recorded.
{
"id": "usage_67c9...",
"amount": 10.5
}Audit Usage History
Retrieve a paginated history of all usage events reported for a specific subscription.
customer_subscriptions:readApiAccessPermission::CUSTOMER_SUBSCRIPTIONS_READQuery Parameters
customerIdstringRequiredsubscriptionIdstringRequiredResponses
Paginated usage history retrieved.
{
"type": "CustomerSubscriptionUsages",
"elements": []
}Ad-hoc Charges
Charges represent immediate, non-recurring billable events created independently of a subscription's billing cycle (e.g., setup fees, professional services).
Create One-Time Charge
Generate an ad-hoc charge. If the capture flag is set to true, the system will immediately attempt to authorize and capture the funds using the customer's primary payment method.
charges:writeApiAccessPermission::CHARGES_WRITEQuery Parameters
customerIdstringRequiredRequest Body
{
"charge": {
"capture": true,
"services": [
{
"name": "Technical Consulting",
"amount": 250,
"taxProfile": "TAX_SERVICES_22"
}
]
}
}charge.capturebooleancharge.servicesarrayRequiredcharge.instructionstringResponses
Charge created and payment status returned.
{
"status": "success",
"id": "ch_777",
"captured": true
}List Organizational Charges
Retrieve all charges (including pending and failed attempts) associated with a specific customer profile.
charges:readApiAccessPermission::CHARGES_READQuery Parameters
customerIdstringRequiredResponses
List of customer-specific charges.
{
"type": "CustomerCharges",
"elements": []
}Charge Details and Reconciliation
Retrieve detailed metadata for a specific charge, including its internal lifecycle state and tax calculations.
charges:readApiAccessPermission::CHARGES_READQuery Parameters
customerIdstringRequiredchargeIdstringRequiredResponses
Full charge metadata.
{
"id": "ch_777",
"customer_id": "cust_123",
"total": 305,
"type": "one-shot",
"status": "paid",
"statuses": {
"pending": false,
"paid": true,
"refunded": false
},
"date": {
"date": "2026-03-01 10:00:00.000000",
"timezone": "+00:00"
}
}External Invoice Synchronization
Manage links to external accounting systems (e.g., Fatture in Cloud) for each charge.
Retrieve Invoice Metadata
charges:readApiAccessPermission::CHARGES_READQuery Parameters
customerIdstringRequiredchargeIdstringRequiredResponses
Invoice synchronization details.
{
"number": "INV-2026-001",
"date": "2026-03-01",
"source": "accounting-system",
"link": "https://docs.acme.com/inv/123.pdf"
}Persist Invoice Metadata
charges:writeApiAccessPermission::CHARGES_WRITEQuery Parameters
customerIdstringRequiredchargeIdstringRequiredRequest Body
{
"invoice": {
"number": "INV-2026-001",
"date": "2026-03-01",
"source": "erp-system",
"link": "https://erp.example.com/invoices/991.pdf"
}
}invoice.numberstringRequiredinvoice.datestringRequiredinvoice.sourcestringinvoice.linkstringResponses
{
"status": "success"
}Global Charge Resolution
Resolve charge details using only the global unique identifier. This is optimized for callback handlers and webhook reconciliation modules.
charges:readApiAccessPermission::CHARGES_READQuery Parameters
chargeIdstringRequiredResponses
Charge metadata resolved globally.
{
"id": "ch_777",
"customer_id": "cust_123"
}