Skip to main content

Migrating to v3 of the API

v3 of the Salable API has a much more simplified implementation. Data access patterns are now aligned across all types of subscriptions meaning less endpoints are required to achieve tasks.

It is our recommendation that all customers migrate to v3 as soon as possible.

Overview

How do I use v3 of the API?

To start using the new and improved V3 endpoints, provide a version header with a value of v3 to each request you send to the API.

How do I use v3 in the Node SDK?

Update the package to v5.0.0. Replace the Salable class with the new initSalable function and set the version to v3. In v5.0.0 the API version v2 will still be supported. For more information read the v5.0.0 changelog.

v4.0.0 implementation

const salable = new Salable('your-api-key', 'v2');

v5.0.0 implementation

const salableV2 = initSalable('your-api-key', 'v2'); // v2 still supported
const salableV3 = initSalable('your-api-key', 'v3');

License vs Seat

The term ‘license’ has been replaced with ‘seat’. Previously, it was confusing referring to 'license' for flat-rate and usage-based subscriptions and then only using 'seat' for per-seat subscriptions. Using 'seat' aligns our documentation, API and app across all subscription types.

Critical breaking changes

License endpoints deprecated

In v3, all license endpoints have been removed. Previously, seats created without a checkout did not have a parent subscription and had to be managed through the license endpoints. In contrast, seats created through a checkout always had a parent subscription and were managed through the subscription endpoints. This inconsistency created two different implementation paths.

For consistency, we have now created a parent subscription for all seats, meaning all seats are now managed through the subscription endpoints. This change simplifies API implementation by providing a single, consistent approach and eliminates the need for separate license endpoints.

Capabilities deprecated

Capabilities used to be stored on the License at the point of creation with no way of editing them. Instead, we have now opted to use the plan's features which allow you to update a grantee’s access on-the-fly through the Salable dashboard.

License check deprecated

The license check endpoint has been replaced with a new endpoint called entitlements check. Any reference to capabilities in the response has been replaced with features. The principle of the endpoint remains unchanged from v2, it still checks what the grantee(s) has access to. To learn more on how to migrate from capabilities to features read this detailed guide. To use the new endpoint, ensure your api key has the entitlements:check scope.

v2 Response
{
"signature": "3044022004f243b5bb524689498537ff6bf865d847b8f6c6f65db687036814281fe4c1cc022043e645238050c587b6a79343a4acad58a925d3fbf3ba914d2f4c38a1f8439993",
"capabilities": [
{
"capability": "plan_name",
"expiry": "2024-07-09T20:08:26.685Z"
}
]
}
v3 Response
{
"signature": "3044022004f243b5bb524689498537ff6bf865d847b8f6c6f65db687036814281fe4c1cc022043e645238050c587b6a79343a4acad58a925d3fbf3ba914d2f4c38a1f8439993",
"features": [
{
"feature": "plan_name",
"expiry": "2024-07-09T20:08:26.685Z"
}
]
}
v3 Implementation
import { getGrantee } from '@salable/js';

const { hasFeature } = await getGrantee({
apiKey: 'your-salable-api-key',
productUuid: 'your-products-uuid',
granteeId: 'your-grantees-id',
});

// Check for a feature
const isUserLicensedToPerformAction = hasFeature('csv-export');
// or a plan
const isUserLicensedToPerformAction = hasFeature('pro');

Breaking changes on existing endpoints

Get All Products

The get all products endpoint now uses cursor based pagination.

v2 Response
[
// products...
]
v3 Response
{
"first": "",
"last": "",
"data": [
// products...
],
}

Deprecated endpoints and Node SDK version updates

Some endpoints have been replaced in v3. Note the examples include v5.0.0 of the Node SDK.

Product

Product Features

Moved to a new paginated get features endpoint with the productUuid filter applied.

v2 Response
[
// features...
]
v3 Response
{
"first": "",
"last": "",
"data": [
// features...
],
}
v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
const productFeatures = await salable.features.getAll({
productUuid: 'your-product-id'
});

Product Currencies

Moved to expand currencies on the get product endpoint.

v2 Response
[
// ...product currencies
]
v3 Response
{
// ...product data
"currencies": [
// ...product currencies
],
}
v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
const productWithCurrencies = await salable.products.getOne('your-product-id', {
expand: ['currencies']
});

Product Plans

Moved to a new get plans endpoint with the productUuid filter applied.

v2 Response
[
// ...product plans
]
v3 Response
{
// ...product data
"plans": [
// ...product plans
],
}
v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
const plans = await salable.plans.getAll({
productUuid: 'your-product-id'
});

Product Capabilities

Endpoint removed in v3.

Plan

Plan Features

Moved to expand features on the get plan endpoint.

v2 Response
[
// ...plan features
]
v3 Response
{
// ...plan data
"features": [
// ...plan features
],
}
v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
const planWithFeatures = await salable.plans.getOne('your-plan-id', {
expand: ['features']
});

Plan Currencies

Moved to expand currencies on the get plan endpoint.

v2 Response
[
// ...plan currencies
]
v3 Response
{
// ...plan data
"currencies": [
// ...plan currencies
],
}
v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
const planWithCurrencies = await salable.plans.getOne('your-plan-id', {
expand: ['currencies']
});

Plan Capabilities

Endpoint removed in v3.

Subscription

Remove Seats

The remove seats request body remains the same in v3. Moving it to the POST endpoint means adding and removing seats are now performed on the same endpoint.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.updateSeatCount('your-subscription-id', {
decrement: 1
});

License (Seat)

Licenses (Seats) Count

Moved to subscriptions get count endpoint, this returns an aggregate count of all seats on the subscription that are not status CANCELED

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.getSeatCount('your-subscription-id');

Licenses Get All (Seats)

Moved to subscriptions get seats endpoint, this returns all seats on the subscription that are not status CANCELED.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.getSeats('your-subscription-id');

Update License (Seat)

Moved to update all subscription endpoint. Fields that apply to both seats and the subscription will both be updated. For example, if the subscription expiryDate was updated this would cascade down to the child seats too.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.update('your-subscription-id', {
expiryDate: new Date()
});

caution

The below deprecated license endpoints are only applicable to vendors that have created licenses (seats) WITHOUT a checkout.

Create License (Seat)

Creating a subscription will create a child seat. The granteeId will be applied to the seat, the owner field will populate the owner on the subscription and the purchaser on the seat.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.create({
planUuid: 'your-plan-id',
owner: 'owner-id',
granteeId: 'userId_1',
});

Licenses (Seats) By Purchaser

Moved to get all subscriptions with the filter owner applied. The v3 response uses cursor based pagination.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.getAll({
owner: 'owner-id'
});

License (Seat) By UUID

Endpoint removed in v3.

Update Many Licenses (Seats)

In v3, the only way to update many seats is to use the subscriptions manage seats endpoint. These updates are limited to assigning, unassigning or replacing the seat's granteeId.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.manageSeats('your-subscription-id', [
{ assign: 'new-grantee-id' },
{ unassign: 'old-grantee-id' },
{
replace: 'grantee-id-to-be-replaced',
newGranteeId: 'replaced-grantee-id'
},
]);

Cancel License (Seat)

You can no longer cancel a seat directly, to cancel a seat, cancel it's parent subscription. If you need to remove a seat on a per-seat subscription, first unassign it, then remove it by updating the seat count on the subscription.

v3 Implementation
import { initSalable } from '@salable/node-sdk';

const salable = initSalable('your-salable-api-key', 'v3');
await salable.subscriptions.cancel('your-subscription-uuid', {
when: 'now'
});

Cancel Many Licenses (seats)

When you cancel a subscription, all of it's child seats will also cancel. For example, if you were to cancel a per-seat subscription with five active seats, all five seats would cancel along with the parent subscription. Many subscriptions cannot be cancelled in the same request.

Renamed endpoints

Some endpoints were missing hyphens in their url paths. This has been addressed, the functionality of the endpoints remain unchanged. The method names in v5.0.0 of the Node SDK are also unchanged.

Get product pricing table

v2 - GET /products/your-product-id/pricingtable
v3 - GET /products/your-product-id/pricing-table

v2 - GET /plans/your-plan-id/checkoutlink
v3 - GET /plans/your-plan-id/checkout-link

v2 - 'GET /subscriptions/your-subscription-id/updatepaymentlink
v3 - 'GET /subscriptions/your-subscription-id/update-payment-link

v2 - GET /subscriptions/{uuid}/cancelpaymentlink
v3 - GET /subscriptions/{uuid}/cancel-payment-link

Deprecated fields

Fields that have been removed in v3.

Product

  • name
  • appType

Plan

  • planType
  • name
  • trialDays - use evalDays
  • evaluation - this can be determined if evalDays is more than 0
  • active - use the plan's status
  • environment
  • paddlePlanId
  • salablePlan

Seat

  • capabilities

Pricing Table

  • customTheme
  • title
  • text
  • theme

Organisation Payment Integration

  • accountData