Managing subscriptions
Administer auto-renewable subscription seats for your organization.
Overview
The Asset Management API provides dedicated endpoints for managing auto-renewable subscriptions separately from standard app and book assets. Unlike traditional assets that have a single pool of assigned and available seats, subscriptions maintain seat counts broken down by renewal state. Each seat is either renewing, meaning it automatically renews at the end of its current billing period, or expiring, meaning it doesn’t renew and becomes inactive when the current billing period ends.
This distinction matters when planning seat allocation. Renewing seats represent ongoing commitments, while expiring seats represent capacity that your organization loses at the end of the current billing period. The API surfaces these states across all subscription endpoints, from seat inventory queries to individual assignment records.
Subscriptions are identified by two values: a parentAdamId that refers to the parent app in the store, and an adamId that identifies the specific subscription product.
Retrieve subscription seat counts
Send a GET request to /v2/subscriptions to retrieve the subscriptions your organization manages. Filter results by parentAdamId or adamId, or omit both to retrieve all subscriptions. The response breaks down seat counts by renewal state for both assigned and available seats.
curl --location --request GET 'https://vpp.itunes.apple.com/mdm/v2/subscriptions?parentAdamId=54321' \
--header 'Authorization: Bearer {sToken}'The code above results in a response like the following:
{
"subscriptions": [
{
"parentAdamId": 54321,
"adamId": 12345,
"status": "ACTIVE",
"counts": {
"assigned": {
"renewing": 80,
"expiring": 20
},
"available": {
"renewing": 15,
"expiring": 5
},
"total": {
"renewing": 100,
"expiring": 20
}
}
}
],
"nextCursor": "NjY5MjY0ODEtZTA4ZC00MmRhLTkxYjItMzdmMDI1MTVkYjQy",
"uId": "2049025000431439",
"tokenExpirationDate": "2030-11-08T22:33:22+0000",
"versionId": "3b4b51d3-07f0-5711-bf45-b48df0fec669"
}Each subscription includes a status field that reflects the current state of the subscription license:
Value | Description |
|---|---|
| The subscription is active and seats can be used and assigned. |
| The subscription is no longer active. |
| The subscription is temporarily paused. |
The assigned object shows how many seats currently have user assignments, broken down by the assignment’s renewal state — not the underlying license state. The available object shows how many seats remain unassigned. The total object shows the actual seat inventory your organization owns, regardless of how seats are assigned. Within each, the renewing count represents seats that auto-renew and the expiring count represents seats that don’t.
The assigned and available counts reflect assignment state, and total reflects license ownership. A renewing seat assigned with renewing set to false appears as assigned.expiring, but the underlying license remains in total.renewing. Use total for capacity planning and assigned for utilization tracking. The invariant is: assigned.renewing + assigned.expiring + available.renewing + available.expiring = total.renewing + total.expiring.
The assigned field appears only in the synchronous GET /v2/subscriptions response. SUBSCRIPTION_COUNT notifications include only available and total. To track assigned counts, query the GET /v2/subscriptions endpoint directly rather than deriving them from notifications.
This endpoint uses cursor-based pagination. Pass the nextCursor value as the cursor query parameter in subsequent requests until nextCursor is absent.
The response can include subscriptions that previously had seats but no longer do. These entries appear with all counts set to zero. Your MDM client should handle zero-count subscriptions gracefully rather than treating them as errors.
Query individual subscription assignments
Send a GET request to /v2/subscriptions/assignments to retrieve per-user assignment details. Filter results by parentAdamId, adamId, or clientUserId. Each assignment record includes a renewing Boolean value that reflects the renewal state of the assignment.
curl --location --request GET 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/assignments?adamId=12345' \
--header 'Authorization: Bearer {sToken}'The code above results in a response like the following:
{
"assignments": [
{
"adamId": 12345,
"clientUserId": "user-1",
"renewing": true
},
{
"adamId": 12345,
"clientUserId": "user-2",
"renewing": false
}
],
"nextCursor": "NjY5MjY0ODEtZTA4ZC00MmRhLTkxYjItMzdmMDI1MTVkYjQy",
"uId": "2049025000431439",
"tokenExpirationDate": "2030-11-08T22:33:22+0000",
"versionId": "ab290ee7-02c1-5ba7-aa0a-652bd6e595bc"
}This endpoint uses cursor-based pagination. Pass the nextCursor value as the cursor query parameter in subsequent requests until nextCursor is absent.
A renewing value of true means the user’s assignment auto-renews at the end of the current billing period. A value of false means the assignment expires at the end of its current billing period. Use this information to identify which assignments need attention before they lapse.
Plan seat allocation based on renewal state
The renewal state of a seat determines what happens to it when a subscription period ends or when your MDM client removes an assignment.
When your MDM client disassociates a user from a renewing seat, that seat returns to the available pool as a renewing seat. The organization retains the seat, and your MDM client can create an assignment for another user. The total number of seats the organization owns remains unchanged.
When a user holds an assignment on an expiring seat and the billing period ends, that seat disappears from the pool entirely. The seat doesn’t renew, so it doesn’t return to the available pool. The organization’s total seat count decreases by one.
Consider an organization with 100 subscription seats — 80 assigned as renewing, 15 assigned as expiring, and 5 available.
{
"counts": {
"assigned": { "renewing": 80, "expiring": 15 },
"available": { "renewing": 5, "expiring": 0 },
"total": { "renewing": 100, "expiring": 0 }
}
}The total confirms that all 100 seats are renewing licenses, even though 15 are assigned in expiring mode. If your MDM client disassociates 10 users from renewing seats, those 10 seats move back to the available renewing pool. The organization still has 100 total seats, now with 70 assigned renewing, 15 assigned expiring, and 15 available renewing.
When the billing period ends for the 15 expiring assignments, those assignments end and the underlying seats return to the available pool as renewing seats — because total.renewing remains 100. The organization retains all 100 seats.
Compare this to a scenario where the organization has marked seats for expiration (total.expiring > 0). When those billing periods end, the seats disappear from the pool entirely and total.expiring decreases. Surface total.expiring in your MDM client so administrators can see how many seats the organization will lose at the end of the billing period.
Expose the available expiring counts in your MDM client so administrators can monitor capacity that they’ll lose at the end of the billing period. Administrators can then choose to create expiring assignments from those seats before the period ends, or purchase additional seats to compensate.
Check request size limits
The size limits for a ManageSubscriptionsRequest are dynamic and can change without notice, so you should sync these every 5 minutes. These limits are in ServiceConfigResponse.Limits.
The following keys are specific to ManageSubscriptionsRequest:
maxSubscriptions- The maximum number of unique subscriptions in a manage requestmaxSubscriptionClientUserIds- The maximum number of unique user identifiers in a manage request
A request that exceeds either limit fails with error 9805 (This request exceeds the maximum subscriptions limit. Change the request to stay within the specified limit.). For more information, see Handling error responses.
Associate subscriptions with users
To create assignments for subscription seats, send a POST request to /v2/subscriptions/associate with the subscription Adam IDs, client user IDs, and the desired renewal state. Set the renewing field to true to assign renewing seats to the specified users.
curl --location --request POST 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/associate' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {sToken}' \
--data-raw '{
"adamIds": [12345],
"clientUserIds": ["user-1", "user-2"],
"renewing": true
}'The server processes this request asynchronously and returns an eventId. Use the Events Status endpoint at GET /v2/status with that eventId to track the operation’s progress.
Assign seats in an expiring state
Set the renewing field to false to create assignments using expiring seats. The user receives access to the subscription for the current billing period, but the seat expires at the end of that period without renewal. This is useful when you want to provide temporary access or when you plan to reassign a seat after the current period ends.
{
"adamIds": [12345],
"clientUserIds": ["user-3"],
"renewing": false
}An expiring seat with an assignment still counts toward your organization’s assigned seat total until the period ends. You can check the balance between renewing and expiring seats at any time through the Get Subscriptions endpoint, where the counts object breaks down both assigned and available totals by renewal state.
Disassociate subscriptions from users
To remove assignments, send a POST request to /v2/subscriptions/disassociate. The request supports a deferred field that controls when the disassociation takes effect. When deferred is true, the assignment remains active for the user until the end of the current billing period rather than being removed immediately.
curl --location --request POST 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/disassociate' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {sToken}' \
--data-raw '{
"adamIds": [12345],
"clientUserIds": ["user-1"],
"deferred": true
}'When deferred is false or omitted, the disassociation takes effect as soon as the server processes the request. Use deferred disassociation to avoid disrupting users in the middle of a billing period. The server processes this request asynchronously and returns an eventId. Check the operation’s status at GET /v2/status using that eventId.
Retrieve subscription administrators
The subscription administrator endpoints let your MDM server control which users have administrative access to specific subscriptions. This may be required for some developers’ intended app experiences.
Each administrator record includes the adamId of the subscription they administer, a clientUserId identifying the administrator, and an idHash for the user. When you include user state information, the response also contains a userStatus field that reflects the administrator’s current association state.
Send a GET request to /v2/subscriptions/admins to retrieve the administrators for subscriptions your organization manages. Filter results by one or more adamId values to see administrators for specific subscriptions. Set the includeUserState query parameter to true to include each administrator’s user status in the response.
curl --location --request GET 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/admins?adamId=12345&includeUserState=true' \
--header 'Authorization: Bearer {sToken}'The code above results in a response like the following:
{
"admins": [
{
"adamId": 12345,
"clientUserId": "vpp-user",
"idHash": "rRVS8rlBrJjRqYwL8aNGDJG2CbU=",
"userStatus": "Associated"
}
],
"nextCursor": "NjY5MjY0ODEtZTA4ZC00MmRhLTkxYjItMzdmMDI1MTVkYjQy",
"uId": "2049025000431439",
"tokenExpirationDate": "2030-11-08T22:33:22+0000",
"versionId": "f5897284-ed94-510f-8914-3b88c9c67799"
}This endpoint uses cursor-based pagination. Pass the nextCursor value as the cursor query parameter in subsequent requests until nextCursor is absent.
Add subscription administrators
Send a POST request to /v2/subscriptions/admins/add to designate users as administrators for specific subscriptions. The request body requires an adamIds array of subscription identifiers and a clientUserIds array of user identifiers.
curl --location --request POST 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/admins/add' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {sToken}' \
--data-raw '{
"adamIds": [12345],
"clientUserIds": ["vpp-user"]
}'The server processes this request synchronously and returns a confirmation response.
{
"uId": "2049025000431439",
"tokenExpirationDate": "2030-11-08T22:33:22+0000",
"versionId": "f5897284-ed94-510f-8914-3b88c9c67799"
}Remove subscription administrators
Send a POST request to /v2/subscriptions/admins/remove to revoke administrator access from users for specific subscriptions. The request body requires an adamIds array of subscription identifiers and a clientUserIds array of user identifiers.
curl --location --request POST 'https://vpp.itunes.apple.com/mdm/v2/subscriptions/admins/remove' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {sToken}' \
--data-raw '{
"adamIds": [12345],
"clientUserIds": ["vpp-user"]
}'The server processes this request synchronously and returns a confirmation response.
{
"uId": "2049025000431439",
"tokenExpirationDate": "2030-11-08T22:33:22+0000",
"versionId": "f5897284-ed94-510f-8914-3b88c9c67799"
}