Third-party API integration
How a third-party application authenticates with an M2M token and calls the Subrite API to find, list, create and update members, create subscriptions, and read packages, podcasts and assets.
Overview#
This guide helps third-party applications integrate with the Subrite API and use the correct API calls to meet business needs. It gives examples of key business operations you can perform with the Subrite API.
Scope of the integration#
Third-party applications that integrate with the Subrite API should keep existing functionality working while ensuring smooth operations. The tasks to perform are defined by business requirements and may include:
- Fetching member information
- Checking if an email exists before creating a member
- Updating an existing member with a new membership
- Creating a new member with a new membership
- Updating custom properties
The guide is meant to help any third-party service provider integrate with the Subrite API efficiently, with a seamless user experience and consistent data.
Prerequisites#
Before you integrate with the Subrite API, make sure you meet the following requirements.
Obtain an M2M access token#
The Subrite API requires an M2M (machine-to-machine) access token for authentication. Generate the token in the environment you are integrating with:
| Environment | Frontend | API |
|---|---|---|
| Stage | *.subrite.no (contact Subrite to get access) | stage.api.subrite.no |
| Production | minside.*.no (contact Subrite to get access) | api.subrite.no |
Create an M2M token that grants Member, Subscription and Content permissions. You receive the token when you click Save.

API details#
This section describes the API operations available for third-party integrations.
Filter members#
To find an existing member by email or custom properties, use the following API call:
Endpoint#
POST /api/v1/members/filterHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
{
"filters": [
{
"field": "updatedAt",
"operator": "gte",
"value": "2024-01-08T07:02:23.742Z"
},
{
"field": "email",
"operator": "eq",
"value": "example@email.no"
},
{
"field": "customProperty.propertyKey",
"operator": "eq",
"value": 1144
},
{
"field": "subscriptionPlanId",
"operator": "eq",
"value": 17
},
{
"field": "subscriptionStatus",
"operator": "eq",
"value": "active"
}
]
}Response#
{
"items": [
{
"id": "string", // Updated: memberId is now a string,Ex: "1", "c29a724f-36cf-4584-9d47-1cdde8733f75" etc
"fullName": "string",
"nickName": "string",
"email": "string",
"birthDate": "2024-01-08T07:02:23.742Z",
"phone": "+4793155388", // Updated: Remove countryCode Return Phone along with calling code
"isCompany": true,
"companyName": "string",
"organizationNo": "string",
"memberNumber": 1234, // memberNumber is a number
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}
]
}For all details, see Get all members of the tenant with filter and pagination in the API reference.
List members#
To list the members of the tenant with minimal data and pagination, use an M2M token with the MEMBER READ permission and the following API call:
Endpoint#
POST /api/v1/members/list?page=1&take=20Headers#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
The body is optional and can be used to filter the list.
{
"filters": []
}Filter validation#
Every filter you send must be one this list can apply. All three rejections below return 400, but they carry different codes, so branch on the code rather than the status.
| What you sent | Response |
|---|---|
A field that is not a recognized filter name at all (for example subscriptionPlanStatus) | errors: [{ field: "field", messageCode: "invalidFormat" }] |
A recognized field this endpoint cannot serve: subscriptionStatus, subscriptionPlanId or the dotted customProperty.<name> form. Use POST /api/v1/members/filter for those. Note that this endpoint takes the bare customProperty field with the name in value. | messageCode: "memberFilterFieldUnsupported" |
A recognized field whose value shape the list cannot compare (for example a consent filter missing status) | messageCode: "memberFilterValueInvalid" |
Previously all three were ignored, which meant a request that asked for a narrow slice could come back with every member in the tenant and a matching total count. Send an empty filters array (or no body) when you want the whole list.
Response#
{
"items": [
{
"memberId": "1",
"memberNumber": 1234,
"fullName": "string",
"isCompany": false,
"companyName": "string",
"email": "string",
"phone": "+4793155388",
"createdAt": "2024-01-08T07:02:23.742Z",
"memberEmailStatus": "delivered",
"totalRevenue": 0,
"hasActiveSubscription": true
}
],
"meta": {
"totalItems": 1,
"itemCount": 1,
"itemsPerPage": 20,
"totalPages": 1,
"currentPage": 1
}
}For all details, see Get all members of the tenant with minimal data with pagination in the API reference.
List member subscriptions#
To list the subscriptions of the tenant with pagination, use an M2M token with the SUBSCRIPTION READ permission and the following API call:
Endpoint#
POST /api/v1/members/subscriptions/list?page=1&take=20Headers#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
The body is optional and can be used to filter the list.
{
"filters": []
}Response#
{
"items": [
{
"id": 1,
"payer": "string",
"memberNumber": 1234,
"subscriptionPlan": "string",
"lastPaidAmount": 0,
"recurringPrice": 0,
"autoRenew": true,
"hasCustomPrice": false,
"activeSubscriptionDays": 0,
"nextRenewal": "2024-01-08T07:02:23.742Z",
"paymentProvider": "stripe",
"status": "active",
"scheduledActivationDate": null
}
],
"meta": {
"totalItems": 1,
"itemCount": 1,
"itemsPerPage": 20,
"totalPages": 1,
"currentPage": 1
}
}For all details, see Get all subscriptions of the tenant with pagination in the API reference.
Create or update a member#
When you integrate with the Subrite API, you must first check if a member exists before you decide whether to create a new member or update an existing one.
Step 1: Check if the member exists#
Use the filter members API (described in the previous section) to check if a member exists.
- If the member exists, the response includes the member ID in the
idfield. - Use this ID as
memberIdto update the member instead of creating a new one.
The next sections describe how to create and update members.
Create a member#
To create a new member in Subrite, use the following API request.
Endpoint#
POST /api/v1/membersHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
{
"fullName": "string",
"email": "string",
"phone": "+4712345678", // Updated: Remove countryCode, Send calling code along with phone
"birthDate": "2024-01-08T11:26:36.376Z",
"nickName": "string",
"isCompany": true,
"companyName": "string",
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postCode": "string",
"country": "string",
"addressType": "primary"
},
"customPropertyValues": {}
}Response#
{
"id": "string", // Updated: memberId is now a string,Ex: "1", "c29a724f-36cf-4584-9d47-1cdde8733f75" etc
"fullName": "string",
"email": "string",
"phone": "+4793155388", // Updated: Remove countryCode Return Phone along with calling code
"birthDate": "2024-01-08T11:26:36.376Z",
"nickName": "string",
"isCompany": true,
"companyName": "string",
"memberNumber": 1234, // memberNumber is a number
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postCode": "string",
"country": "string",
"addressType": "primary"
},
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}For all details, see Create a new member in the API reference.
Update a member#
To update an existing member, use the following API request.
Endpoint#
PATCH /api/v1/members/{memberId}Headers#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
{
"fullName": "string",
"email": "string",
"phone": "+4712345678", // Updated: Remove countryCode, Send calling code along with phone
"birthDate": "2024-01-08T11:26:36.376Z",
"nickName": "string",
"isCompany": true,
"companyName": "string",
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postCode": "string",
"country": "string",
"addressType": "primary"
},
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}Response#
{
"id": "string", // Updated: memberId is now a string,Ex: "1", "c29a724f-36cf-4584-9d47-1cdde8733f75" etc
"fullName": "string",
"email": "string",
"phone": "+4793155388", // Updated: Remove countryCode Return Phone along with calling code
"birthDate": "2024-01-08T11:26:36.376Z",
"nickName": "string",
"isCompany": true,
"companyName": "string",
"organizationNo": "string",
"memberNumber": 1234, // memberNumber is a number
"address": {
"line1": "string",
"line2": "string",
"city": "string",
"state": "string",
"postCode": "string",
"country": "string",
"addressType": "primary"
},
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}For all details, see Update a member in the API reference.
Update a member's custom properties#
To update a member's custom properties, use the following API request.
Endpoint#
PATCH /api/v1/members/{memberId}Headers#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
{
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}For all details, see Update a member in the API reference.
Create a subscription#
To subscribe a member to a product package, use the following API request.
Endpoint#
POST /api/v1/product-packages/subscribeHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonBody#
{
"packageId": 17,
"optionalProducts": [
{
"productId": 201,
"quantity": 2,
"shippingCountry": "NO"
},
{
"productId": 202,
"quantity": 1
}
],
"paymentDate": "2024-01-08T07:43:30.122Z",
"memberId": "1",
"reference": ""
}Field descriptions#
packageId(number, required): the ID of the product package.optionalProducts(array, optional): list of optional products with quantity and an optional shipping country.productId(number, required): the ID of the product.quantity(number, required, min: 1): number of units of this product.shippingCountry(string, optional): country code, if the product requires shipping.
paymentDate(ISO 8601 date string, optional): the date of payment (for example"2024-01-08T07:43:30.122Z").memberId(string, required): the ID of the member. Examples:"1","c29a724f-36cf-4584-9d47-1cdde8733f75".reference(string, optional): a custom reference string for the subscription.
For all details, see Create a subscription for a pre-purchased package in the API reference.
Get packages by filters#
To find packages with filters, use an M2M token with the PLANS_AND_PRODUCTS READ permission and the following API call:
Endpoint#
GET /api/v1/product-packages/allHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonQuery parameters (filters)#
All filters are optional. Combine several filters to narrow down the results.
status(string or array, optional): filter packages by status. Accepts one or more values. Available values:draft,published,unpublished. When not provided, packages with all statuses are returned.isAvailableViaSlug(boolean, optional): filter packages that are available via slug. Whentrue, returns only packages that can be accessed through a slug URL.isAvailableOnMyPage(boolean, optional): filter packages that are available on the member's "My Page". Whentrue, returns only packages that members can see and manage from their personal page.isAvailableForManualOrderViaAdmin(boolean, optional): filter packages that are available for manual order by an admin. Whentrue, returns only packages that administrators can order manually on behalf of members.enableGiftSubscription(boolean, optional): filter packages that have gift subscription enabled. Whentrue, returns only packages where the associated subscription plan has gift subscription enabled.
Example requests#
# Filter by single status
GET /api/v1/product-packages/all?status=published
# Filter by multiple statuses
GET /api/v1/product-packages/all?status=published&status=draft
# Combine with other filters
GET /api/v1/product-packages/all?status=published&isAvailableViaSlug=true&isAvailableOnMyPage=trueResponse#
[
{
"id": 17,
"salesTitle": "Premium Package",
"name": "Premium Package",
"slug": "premium-package",
"image": "https://example.com/image.jpg",
"description": ["Package description"],
"price": 299.0,
"recurringPrice": 299.0,
"status": "publish",
"isAvailableViaSlug": true,
"isAvailableOnMyPage": true,
"isAvailableForManualOrderViaAdmin": false,
"products": [
{
"productId": 201,
"name": "Product Name",
"price": 299.0,
"recurringPrice": 299.0,
"currency": "NOK",
"tax": 25,
"isOptional": false,
"productCategory": "digital_subscription"
}
],
"subscriptionCount": 150
}
]For all details, see Get packages by filters in the API reference.
Get package details by ID#
To get the details of a specific package by ID, use an M2M token with the PLANS_AND_PRODUCTS READ permission and the following API call:
Endpoint#
GET /api/v1/product-packages/details/:idHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonPath parameters#
id(number, required): the ID of the package.
Example request#
GET /api/v1/product-packages/details/17Response#
{
"id": 17,
"salesTitle": "Premium Package",
"name": "Premium Package",
"slug": "premium-package",
"image": "https://example.com/image.jpg",
"description": ["Package description"],
"price": 299.0,
"recurringPrice": 299.0,
"status": "publish",
"isAvailableViaSlug": true,
"isAvailableOnMyPage": true,
"isAvailableForManualOrderViaAdmin": false,
"billingPeriod": "monthly",
"products": [
{
"productId": 201,
"name": "Product Name",
"price": 299.0,
"recurringPrice": 299.0,
"basePrice": 299.0,
"currency": "NOK",
"tax": 25,
"isOptional": false,
"productCategory": "digital_subscription",
"properties": null,
"recurringProperties": null,
"description": "Product description",
"shippingCharges": []
}
],
"subscriptionCount": 150,
"paymentProviders": ["stripe", "vipps"],
"customInputFields": {
"memberType": true,
"invoiceAddress": true,
"city": true
}
}For all details, see Get package details by ID in the API reference.
Get all available podcasts#
To get all configured podcasts, use an M2M token with the CONTENT READ permission and the following API call:
Endpoint#
GET /api/v1/podcast-adminHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonResponse#
[
{
"id": 1,
"createdAt": "2024-12-03T04:03:05.156Z",
"updatedAt": "2025-10-22T04:57:01.219Z",
"deletedAt": null,
"metaData": null,
"name": "Test Name",
"rssFeedUrl": "",
"imageUrl": "",
"showUrl": "",
"description": "Test description",
"sortOrder": 0,
"tenantId": 0
}
]For all details, see Get all podcasts of the tenant in the API reference.
Get podcast details#
To get the details of a specific podcast by ID, use an M2M token with the CONTENT READ permission and the following API call:
Endpoint#
GET /api/v1/podcast-admin/:idHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonResponse#
{
"id": 1,
"createdAt": "2024-12-03T04:03:05.156Z",
"updatedAt": "2025-10-22T04:57:01.219Z",
"deletedAt": null,
"metaData": null,
"name": "Test Name",
"rssFeedUrl": "",
"imageUrl": "",
"showUrl": "",
"description": "Test description",
"sortOrder": 0,
"tenantId": 0
}For all details, see Get podcast details by ID in the API reference.
List asset folders#
To list the tenant's asset folders with per-folder file counts and tenant-wide usage totals, use an M2M token with the CONTENT READ permission and the following API call. Assets are files (images, PDFs, documents and so on) uploaded to the tenant's media library and served publicly from the tenant's dedicated CDN domain.
Endpoint#
GET /api/v1/asset-admin/foldersHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonResponse#
{
"folders": [
{
"id": 1,
"name": "Brand",
"slug": "brand",
"fileCount": 6,
"createdAt": "2026-02-02T09:15:00.000Z"
}
],
"totalFileCount": 24,
"totalSizeBytes": 18452312,
"cdnDomain": "assets.subrite.no"
}folders: the tenant's folders.slugis the first path segment of every file's public URL in that folder.totalFileCountandtotalSizeBytes: totals across the whole tenant, including root-level files (files not in any folder).cdnDomain: the domain files are served from. The public URL of a file ishttps://{cdnDomain}/{tenantId}/{folderSlug}/{fileName}(root-level files omit the{folderSlug}segment).
For all details, see List folders with counts and usage totals in the API reference.
List assets#
To list, search and sort the tenant's assets with pagination, use an M2M token with the CONTENT READ permission and the following API call. Search and sort span all of the tenant's assets.
Endpoint#
GET /api/v1/asset-admin/assets?page=1&take=24Headers#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonQuery parameters#
All parameters are optional.
page(number, default: 1): page number.take(number, 1 to 50, default: 24): page size.folderId(number, optional): restrict to one folder. Omit it for all files; pass0orrootfor root-level files (files not in any folder).kinds(string, optional): restrict to certain file kinds, for examplekinds=image,svg(or a repeatedkindsparameter). Any ofimage,svg,pdf,video,audio,font,doc.search(string, optional): case-insensitive match on the display name.sort(string, optional): one ofname,size,uploaded(default:uploaded).order(string, optional):ascordesc(default:desc).
Response#
{
"items": [
{
"id": 42,
"folderId": 1,
"title": "logo-subrite-primary.svg",
"name": "logo-subrite-primary.svg",
"url": "https://assets.subrite.no/17/brand/logo-subrite-primary.svg",
"kind": "svg",
"mimeType": "image/svg+xml",
"sizeBytes": 8192,
"width": 512,
"height": 512,
"altText": null,
"createdAt": "2026-02-02T09:15:00.000Z"
}
],
"meta": {
"totalItems": 24,
"itemCount": 24,
"itemsPerPage": 24,
"totalPages": 1,
"currentPage": 1
}
}folderId:nullfor root-level files.title: the human-readable display name.name: the URL-safe file name (the last segment ofurl).url: the public CDN URL to link to or embed directly.kind: one ofimage,svg,pdf,video,audio,font,doc.widthandheight: present for images and SVGs, otherwisenull.
For all details, see List/search/sort assets in the API reference.
Get an asset#
To fetch a single asset by ID, use an M2M token with the CONTENT READ permission and the following API call:
Endpoint#
GET /api/v1/asset-admin/assets/:idHeaders#
Authorization: Bearer {m2m_token}
Content-Type: application/jsonPath parameters#
id(number, required): the ID of the asset.
Response#
{
"id": 42,
"folderId": 1,
"title": "logo-subrite-primary.svg",
"name": "logo-subrite-primary.svg",
"url": "https://assets.subrite.no/17/brand/logo-subrite-primary.svg",
"kind": "svg",
"mimeType": "image/svg+xml",
"sizeBytes": 8192,
"width": 512,
"height": 512,
"altText": null,
"createdAt": "2026-02-02T09:15:00.000Z"
}For all details, see Get a single asset in the API reference.
Download an asset#
To download an asset's file bytes, use an M2M token with the CONTENT READ permission and the following API call. Unlike the public url (which the CDN serves inline), this endpoint streams the object with a Content-Disposition: attachment header, so browsers and HTTP clients save it as a file using its display name.
Endpoint#
GET /api/v1/asset-admin/assets/:id/downloadHeaders#
Authorization: Bearer {m2m_token}Path parameters#
id(number, required): the ID of the asset.
Response#
The raw file bytes, with these headers:
Content-Type: the asset's MIME type (for exampleimage/svg+xmlorapplication/pdf).Content-Disposition:attachment; filename="..."using the asset's display name.
For all details, see Download an asset in the API reference.
Summary of key API calls#
| API operation | API endpoint |
|---|---|
| Fetch member info | POST /api/v1/members/filter |
| Check if email exists | POST /api/v1/members/filter |
| List members | POST /api/v1/members/list |
| List member subscriptions | POST /api/v1/members/subscriptions/list |
| Update existing member | PATCH /api/v1/members/{memberId} |
| Create new member | POST /api/v1/members |
| Create subscription | POST /api/v1/product-packages/subscribe |
| Update custom properties | PATCH /api/v1/members/{memberId} |
| Fetch packages by filters | GET /api/v1/product-packages/all |
| Fetch package details | GET /api/v1/product-packages/details/{id} |
| Fetch all podcasts | GET /api/v1/podcast-admin |
| Fetch podcast details | GET /api/v1/podcast-admin/{id} |
| List asset folders | GET /api/v1/asset-admin/folders |
| List assets | GET /api/v1/asset-admin/assets |
| Fetch an asset | GET /api/v1/asset-admin/assets/{id} |
| Download an asset | GET /api/v1/asset-admin/assets/{id}/download |
Changelog#
Filter members API#
- API call:
POST /api/v1/members/filter - Response update:
"phone": "+4793155388" // Removed countryCode, returning phone along with calling codeCreate member API#
- API call:
POST /api/v1/members - Response update:
"phone": "+4793155388" // Removed countryCode, returning phone along with calling codeUpdate member API#
- API call:
PATCH /api/v1/members/{memberId} - Response update:
"phone": "+4793155388" // Removed countryCode, returning phone along with calling codememberId type change#
- API call:
PATCH /api/v1/members/{memberId} - Response update:
"memberId": "1" // Updated: memberId is now a string,Ex: "1", "c29a724f-36cf-4584-9d47-1cdde8733f75" etc