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.
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.
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.
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:
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.
Before you integrate with the Subrite API, make sure you meet the following requirements.
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.

This section describes the API operations available for third-party integrations.
To find an existing member by email or custom properties, use the following API call:
POST /api/v1/members/filterAuthorization: Bearer {m2m_token}
Content-Type: application/json{
"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"
}
]
}{
"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 in the API reference.
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:
POST /api/v1/members/list?page=1&take=20Authorization: Bearer {m2m_token}
Content-Type: application/jsonThe body is optional and can be used to filter the list.
{
"filters": []
}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.
{
"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 in the API reference.
To list the subscriptions of the tenant with pagination, use an M2M token with the SUBSCRIPTION READ permission and the following API call:
POST /api/v1/members/subscriptions/list?page=1&take=20Authorization: Bearer {m2m_token}
Content-Type: application/jsonThe body is optional and can be used to filter the list.
{
"filters": []
}{
"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 in the API reference.
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.
Use the filter members API (described in the previous section) to check if a member exists.
id field.memberId to update the member instead of creating a new one.The next sections describe how to create and update members.
To create a new member in Subrite, use the following API request.
POST /api/v1/membersAuthorization: Bearer {m2m_token}
Content-Type: application/json{
"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": {}
}{
"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 in the API reference.
To update an existing member, use the following API request.
PATCH /api/v1/members/{memberId}Authorization: Bearer {m2m_token}
Content-Type: application/json{
"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"
}
}{
"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 in the API reference.
To update a member's custom properties, use the following API request.
PATCH /api/v1/members/{memberId}Authorization: Bearer {m2m_token}
Content-Type: application/json{
"customPropertyValues": {
"propertyKey": "propertyValue"
}
}For all details, see in the API reference.
To subscribe a member to a product package, use the following API request.
POST /api/v1/product-packages/subscribeAuthorization: Bearer {m2m_token}
Content-Type: application/json{
"packageId": 17,
"optionalProducts": [
{
"productId": 201,
"quantity": 2,
"shippingCountry": "NO"
},
{
"productId": 202,
"quantity": 1
}
],
"paymentDate": "2024-01-08T07:43:30.122Z",
"memberId": "1",
"reference": ""
}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 in the API reference.
To find packages with filters, use an M2M token with the PLANS_AND_PRODUCTS READ permission and the following API call:
GET /api/v1/product-packages/allAuthorization: Bearer {m2m_token}
Content-Type: application/jsonAll 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. When true, 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". When true, 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. When true, returns only packages that administrators can order manually on behalf of members.enableGiftSubscription (boolean, optional): filter packages that have gift subscription enabled. When true, returns only packages where the associated subscription plan has gift subscription enabled.# 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=true[
{
"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 in the API reference.
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:
GET /api/v1/product-packages/details/:idAuthorization: Bearer {m2m_token}
Content-Type: application/jsonid (number, required): the ID of the package.GET /api/v1/product-packages/details/17{
"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 in the API reference.
To get all configured podcasts, use an M2M token with the CONTENT READ permission and the following API call:
GET /api/v1/podcast-adminAuthorization: Bearer {m2m_token}
Content-Type: application/json[
{
"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 in the API reference.
To get the details of a specific podcast by ID, use an M2M token with the CONTENT READ permission and the following API call:
GET /api/v1/podcast-admin/:idAuthorization: Bearer {m2m_token}
Content-Type: application/json{
"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 in the API reference.
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.
GET /api/v1/asset-admin/foldersAuthorization: Bearer {m2m_token}
Content-Type: application/json{
"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. slug is the first path segment of every file's public URL in that folder.totalFileCount and totalSizeBytes: 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 is https://{cdnDomain}/{tenantId}/{folderSlug}/{fileName} (root-level files omit the {folderSlug} segment).For all details, see in the API reference.
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.
GET /api/v1/asset-admin/assets?page=1&take=24Authorization: Bearer {m2m_token}
Content-Type: application/jsonAll 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; pass 0 or root for root-level files (files not in any folder).kinds (string, optional): restrict to certain file kinds, for example kinds=image,svg (or a repeated kinds parameter). Any of image, svg, pdf, video, audio, font, doc.search (string, optional): case-insensitive match on the display name.sort (string, optional): one of name, size, uploaded (default: uploaded).order (string, optional): asc or desc (default: desc).{
"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: null for root-level files.title: the human-readable display name. name: the URL-safe file name (the last segment of url).url: the public CDN URL to link to or embed directly.kind: one of image, svg, pdf, video, audio, font, doc.width and height: present for images and SVGs, otherwise null.For all details, see in the API reference.
To fetch a single asset by ID, use an M2M token with the CONTENT READ permission and the following API call:
GET /api/v1/asset-admin/assets/:idAuthorization: Bearer {m2m_token}
Content-Type: application/jsonid (number, required): the ID of the asset.{
"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 in the API reference.
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.
GET /api/v1/asset-admin/assets/:id/downloadAuthorization: Bearer {m2m_token}id (number, required): the ID of the asset.The raw file bytes, with these headers:
Content-Type: the asset's MIME type (for example image/svg+xml or application/pdf).Content-Disposition: attachment; filename="..." using the asset's display name.For all details, see in the API reference.
| 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 |
POST /api/v1/members/filter"phone": "+4793155388" // Removed countryCode, returning phone along with calling codePOST /api/v1/members"phone": "+4793155388" // Removed countryCode, returning phone along with calling codePATCH /api/v1/members/{memberId}"phone": "+4793155388" // Removed countryCode, returning phone along with calling codePATCH /api/v1/members/{memberId}"memberId": "1" // Updated: memberId is now a string,Ex: "1", "c29a724f-36cf-4584-9d47-1cdde8733f75" etcPOST /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 |