Subscriptions
The Subscription API Resource
The subscription model contains all the information about recurring billing relationships with customers.
Properties
| Name | Type | Description |
|---|---|---|
id | string | Unique identifier for the subscription (starts with subscription_). |
resource | string | The resource type. Always subscription. |
customerId | string | ID of the customer who owns this subscription. |
subscriptionPlanId | string | ID of the subscription plan this subscription is based on (starts with subscription_plan_). |
testmode | boolean | Whether this resource is in test mode. |
name | string | The name for the subscription (from the plan). |
description | string | The description for the subscription. |
billingAddress | object | The customer billing address for the subscription. Includes fullName, companyName, taxId, streetAndNumber, streetAdditional, city, region, postalCode, and country. |
basePrice | Money | The base price per billing cycle before taxes. A Money object with value (decimal string) and currency (ISO 4217 code). |
quantity | integer | The quantity for the subscription (e.g., number of seats). |
interval | string | The billing interval. Can be day, week, month, or year. |
intervalCount | integer | The interval count, e.g., "3" for charging every 3 months. |
status | string | The status for the subscription. Can be created, trial, active, canceled, on_grace_period, or paused. |
mandate | object | null | The payment method (mandate) on file. Contains method (e.g. card, sepa_debit, paypal, bacs_debit) and maskedIdentifier (e.g. the last 4 digits). Null if no mandate is set. |
startedAt | string | null | When the subscription started (ISO 8601 format). |
endedAt | string | null | When the subscription actually ended (ISO 8601 format). Stays null during a cancellation grace period — it is only set once the subscription has fully ended. |
canceledAt | string | null | When the subscription was canceled (ISO 8601 format). Set while in on_grace_period, and cleared again if the subscription is resumed. Null if not canceled. |
renewedAt | string | null | When the subscription was last renewed (ISO 8601 format). |
renewedUntil | string | null | Current billing period end date (ISO 8601 format). |
nextRenewalAt | string | null | When the next renewal will be attempted (ISO 8601 format). Null if subscription is canceled or ended. |
trialUntil | string | null | When the trial period ends (ISO 8601 format). Null if not in trial or trial has ended. |
links | object | HATEOAS links to related resources. Contains self and customer links. |
List all subscriptions
GET /v1/subscriptions
This endpoint allows you to retrieve a paginated list of all subscriptions across your account.
Optional attributes
| Name | Type | Description |
|---|---|---|
limit | integer | The number of subscriptions to return (default: 10, max: 100). |
startingAfter | string | A cursor for use in pagination. Returns results after this subscription ID. |
endingBefore | string | A cursor for use in pagination. Returns results before this subscription ID. |
curl -G https://api.vatly.com/v1/subscriptions \
-H "Authorization: Bearer live_your_api_key_here" \
-d limit=10
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscriptions = $vatly->subscriptions->page();
{
"data": [
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"testmode": false,
"name": "Pro Monthly",
"description": "Full access to all Pro features",
"billingAddress": {
"fullName": "John Doe",
"companyName": "Acme Corp",
"taxId": null,
"streetAndNumber": "123 Main Street",
"streetAdditional": null,
"city": "Berlin",
"region": null,
"postalCode": "10115",
"country": "DE"
},
"basePrice": {
"value": "29.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "month",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2024-03-15T10:30:00Z",
"nextRenewalAt": "2024-03-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
},
{
"id": "subscription_Wt5mNvBxKw7YcZaEjLhR",
"resource": "subscription",
"customerId": "customer_Mn6xBtPvKw2RjTgYcZaE",
"testmode": false,
"name": "Enterprise Yearly",
"description": "Enterprise features with priority support",
"billingAddress": {
"fullName": "Jane Smith",
"companyName": "TechCorp Ltd",
"taxId": "GB123456789",
"streetAndNumber": "456 Tech Lane",
"streetAdditional": null,
"city": "London",
"region": null,
"postalCode": "EC1A 1BB",
"country": "GB"
},
"basePrice": {
"value": "990.00",
"currency": "EUR"
},
"quantity": 5,
"interval": "year",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-01T00:00:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-01-01T00:00:00Z",
"renewedUntil": "2025-01-01T00:00:00Z",
"nextRenewalAt": "2025-01-01T00:00:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Wt5mNvBxKw7YcZaEjLhR",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Mn6xBtPvKw2RjTgYcZaE",
"type": "application/json"
}
}
}
],
"count": 2,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions",
"type": "application/json"
},
"next": null,
"prev": null
}
}
Get a subscription
GET /v1/subscriptions/:id
This endpoint allows you to retrieve a specific subscription by its ID.
Parameters
| Name | Type | Description |
|---|---|---|
subscriptionId | string | The unique identifier of the subscription. |
curl https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscription = $vatly->subscriptions->get('subscription_Lp3mNvBxKw7RjTgYcZaE');
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"testmode": false,
"name": "Pro Monthly",
"description": "Full access to all Pro features",
"billingAddress": {
"fullName": "John Doe",
"companyName": "Acme Corp",
"taxId": null,
"streetAndNumber": "123 Main Street",
"streetAdditional": null,
"city": "Berlin",
"region": null,
"postalCode": "10115",
"country": "DE"
},
"basePrice": {
"value": "29.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "month",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2024-03-15T10:30:00Z",
"nextRenewalAt": "2024-03-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}
List customer subscriptions
GET /v1/customers/:customerId/subscriptions
This endpoint allows you to retrieve a paginated list of all subscriptions for a specific customer.
Parameters
| Name | Type | Description |
|---|---|---|
customerId | string | The unique identifier of the customer. |
limit | integer | The number of subscriptions to return (default: 10, max: 100). |
startingAfter | string | A cursor for use in pagination. Returns results after this subscription ID. |
endingBefore | string | A cursor for use in pagination. Returns results before this subscription ID. |
curl -G https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE/subscriptions \
-H "Authorization: Bearer live_your_api_key_here" \
-d limit=10
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscriptions = $vatly->customers->subscriptions('customer_Lp3mNvBxKw7RjTgYcZaE')->page();
{
"data": [
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"testmode": false,
"name": "Pro Monthly",
"description": "Full access to all Pro features",
"billingAddress": {
"fullName": "John Doe",
"companyName": "Acme Corp",
"taxId": null,
"streetAndNumber": "123 Main Street",
"streetAdditional": null,
"city": "Berlin",
"region": null,
"postalCode": "10115",
"country": "DE"
},
"basePrice": {
"value": "29.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "month",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2024-03-15T10:30:00Z",
"nextRenewalAt": "2024-03-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}
],
"count": 1,
"links": {
"self": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE/subscriptions",
"type": "application/json"
},
"next": null,
"prev": null
}
}
Get a customer subscription
GET /v1/customers/:customerId/subscriptions/:subscriptionId
This endpoint allows you to retrieve a specific subscription for a specific customer.
Parameters
| Name | Type | Description |
|---|---|---|
customerId | string | The unique identifier of the customer. |
subscriptionId | string | The unique identifier of the subscription. |
curl https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscription = $vatly->customers->subscriptions('customer_Lp3mNvBxKw7RjTgYcZaE')->get('subscription_Lp3mNvBxKw7RjTgYcZaE');
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"testmode": false,
"name": "Pro Monthly",
"description": "Full access to all Pro features",
"billingAddress": {
"fullName": "John Doe",
"companyName": "Acme Corp",
"taxId": null,
"streetAndNumber": "123 Main Street",
"streetAdditional": null,
"city": "Berlin",
"region": null,
"postalCode": "10115",
"country": "DE"
},
"basePrice": {
"value": "29.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "month",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2024-03-15T10:30:00Z",
"nextRenewalAt": "2024-03-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}
Update a subscription
PATCH /v1/subscriptions/:id
This endpoint allows you to update a subscription. You can change the plan, quantity, or recurring price, and control proration and timing.
Optional attributes
At least one of subscriptionPlanId, quantity, or price must be provided.
| Name | Type | Description |
|---|---|---|
subscriptionPlanId | string | The ID of the subscription plan to update to (starts with subscription_plan_). Must match the testmode of the current subscription. |
quantity | integer | The new total quantity for the subscription (e.g. number of seats). This sets the quantity to the given value — it is not added to the current quantity. Must be at least 1. |
price | object | Set a new recurring price while keeping the current plan. A Money object with value (decimal string) and currency (EUR or USD); the currency must match the subscription's own currency. Sent alongside subscriptionPlanId, it overrides the new plan's default price. |
prorate | boolean | Whether to prorate charges for the partial billing period. If true, the customer is credited for unused time on the old plan and charged for remaining time on the new plan. Default: true. |
applyImmediately | boolean | Whether to apply changes immediately or at the end of the current billing period. Default: false. |
invoiceImmediately | boolean | Whether to generate an invoice immediately for proration. Only applies when applyImmediately and prorate are both true. Default: false. |
anchor | date | Set the billing anchor to a specific calendar date (YYYY-MM-DD, interpreted in UTC); the billing cycle is recalculated around it. Cannot be combined with resetAnchor. |
resetAnchor | boolean | Reset the billing anchor to "now" — the moment the update is processed becomes the new cycle anchor and future renewals align to it. Cannot be combined with anchor. Default: false. |
trialUntil | string | Extend or set a trial period until this date (ISO 8601 format). Cannot be combined with anchor. |
curl -X PATCH https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"subscriptionPlanId": "subscription_plan_Wt5mNvBxKw7YcZaEjLhR",
"prorate": true,
"applyImmediately": true
}'
curl -X PATCH https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"price": { "value": "29.00", "currency": "EUR" },
"applyImmediately": true
}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscription = $vatly->subscriptions->update('subscription_Lp3mNvBxKw7RjTgYcZaE', [
'subscriptionPlanId' => 'subscription_plan_Wt5mNvBxKw7YcZaEjLhR',
'prorate' => true,
'applyImmediately' => true,
]);
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"testmode": false,
"name": "Pro Yearly",
"description": "Full access to all Pro features, billed yearly",
"billingAddress": {
"fullName": "John Doe",
"companyName": "Acme Corp",
"taxId": null,
"streetAndNumber": "123 Main Street",
"streetAdditional": null,
"city": "Berlin",
"region": null,
"postalCode": "10115",
"country": "DE"
},
"basePrice": {
"value": "290.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "year",
"intervalCount": 1,
"status": "active",
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2025-02-15T10:30:00Z",
"nextRenewalAt": "2025-02-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}
Create billing update link
POST /v1/subscriptions/:id/billing-update-link
Creates a signed link that the customer can use to update the billing details for this subscription — billing address, VAT number, and company name — via a hosted flow.
Required attributes
| Name | Type | Description |
|---|---|---|
redirectUrlSuccess | string | URL to redirect after successful billing update. |
redirectUrlCanceled | string | URL to redirect if customer cancels the update. |
Optional attributes
| Name | Type | Description |
|---|---|---|
billingAddress | object | Pre-fill billing address fields. Customer can modify these values in the hosted form. |
curl -X POST https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE/billing-update-link \
-H "Authorization: Bearer live_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"redirectUrlSuccess": "https://example.com/billing-updated",
"redirectUrlCanceled": "https://example.com/account/billing",
"billingAddress": {
"companyName": "Acme Corp",
"taxId": "DE123456789"
}
}'
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$response = $vatly->subscriptions->createBillingUpdateLink('subscription_Lp3mNvBxKw7RjTgYcZaE', [
'redirectUrlSuccess' => 'https://example.com/billing-updated',
'redirectUrlCanceled' => 'https://example.com/account/billing',
'billingAddress' => [
'companyName' => 'Acme Corp',
'taxId' => 'DE123456789',
],
]);
// Redirect the customer to the hosted billing update page
header('Location: ' . $response->href, true, 303);
{
"href": "https://vatly.com/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE/billing?token=xyz...",
"type": "text/html"
}
Cancel a subscription
DELETE /v1/subscriptions/:id
This endpoint allows you to cancel a subscription. By default, the subscription will remain active until the end of the current billing period (grace period), after which it will be fully canceled. Set immediately=true to cancel immediately.
During the grace period the subscription's status is on_grace_period and canceledAt is set, while endedAt stays null. A grace-period cancellation can still be reversed with Resume a subscription until the period lapses. A subscription canceled with immediately=true (status canceled) cannot be reactivated — create a new subscription instead.
Optional query parameters
| Name | Type | Description |
|---|---|---|
immediately | boolean | Cancel immediately instead of at period end. Default: false. |
curl -X DELETE https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE \
-H "Authorization: Bearer live_your_api_key_here"
curl -X DELETE "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE?immediately=true" \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$vatly->subscriptions->cancel('subscription_Lp3mNvBxKw7RjTgYcZaE');
Returns 204 No Content on success.
Resume a subscription
POST /v1/subscriptions/:id/resume
Resumes a subscription that was canceled with a grace period, while it is still within that period.
When this works:
- The subscription's
statusison_grace_period. endedAtstaysnullthroughout the grace period — resumability is keyed offstatus, notendedAt.
Result:
- Status returns to
active. - The existing billing cycle and renewal schedule are preserved (no new charge fires immediately) and the original payment mandate remains in effect.
canceledAtis cleared and asubscription.resumedwebhook is delivered.- The refreshed subscription is returned in the response body.
When this does not work (returns 422):
- The subscription was canceled immediately (
statusiscanceled). - The grace period has already lapsed (
statusis nowcanceledandendedAtis set). - The subscription is already active (the no-op is rejected for clarity, not silently ignored).
In all of those cases, create a new subscription instead.
Parameters
| Name | Type | Description |
|---|---|---|
id | string | The unique identifier of the subscription. |
curl -X POST https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE/resume \
-H "Authorization: Bearer live_your_api_key_here"
$vatly = new \Vatly\API\VatlyApiClient();
$vatly->setApiKey('live_your_api_key_here');
$subscription = $vatly->subscriptions->resume('subscription_Lp3mNvBxKw7RjTgYcZaE');
{
"id": "subscription_Lp3mNvBxKw7RjTgYcZaE",
"resource": "subscription",
"customerId": "customer_Lp3mNvBxKw7RjTgYcZaE",
"subscriptionPlanId": "subscription_plan_Rk5pQrSvWm8NjLhYbUcP",
"testmode": false,
"name": "Pro Monthly",
"status": "active",
"basePrice": {
"value": "29.00",
"currency": "EUR"
},
"quantity": 1,
"interval": "month",
"intervalCount": 1,
"startedAt": "2024-01-15T10:30:00Z",
"endedAt": null,
"canceledAt": null,
"renewedAt": "2024-02-15T10:30:00Z",
"renewedUntil": "2024-03-15T10:30:00Z",
"nextRenewalAt": "2024-03-15T10:30:00Z",
"trialUntil": null,
"links": {
"self": {
"href": "https://api.vatly.com/v1/subscriptions/subscription_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
},
"customer": {
"href": "https://api.vatly.com/v1/customers/customer_Lp3mNvBxKw7RjTgYcZaE",
"type": "application/json"
}
}
}