Developers

Developer APIs

Strimm exposes two HTTP APIs for apps and integrations. Use the REST API for new integrations with a { success, data } envelope. Use the Custom Services API for the bundled mobile app template and other legacy-compatible clients that expect a { data } envelope. Claude, ChatGPT, and Grok should use the hosted MCP server instead of calling these APIs directly.

REST API

Token-based API for studio-authenticated apps. Login with username or email, then send the GUID auth token on protected routes.

Base URL

https://newlook2.strimm.com/api/v1/rest

Response envelope

Successful responses:

{
  "success": true,
  "data": { ... }
}

Error responses:

{
  "success": false,
  "error": {
    "code": "invalid_credentials",
    "message": "Login failed."
  }
}

Authentication

POST /auth/login

No auth required. Accepts username or email and password.

Request body

{
  "username": "your_username",
  "password": "your_password"
}

Response data

{
  "authToken": "00000000-0000-0000-0000-000000000000",
  "userId": 123,
  "userName": "creator",
  "email": "creator@example.com",
  "publicUrl": "creator"
}
POST /auth/logout

Revokes the Bearer token sent in the Authorization header.

Apps

GET /apps/{platform}

Returns app configuration for the authenticated user. Platforms: mobile, roku, firetv, appletv, androidtv (case-insensitive).

Example request

GET /api/v1/rest/apps/mobile
Authorization: Bearer {authToken}

Example response

{
  "success": true,
  "data": {
    "appId": 1,
    "platform": "Mobile",
    "appName": "My Network App",
    "channelTubeIds": [42, 58],
    "isPaid": true,
    "storeProducts": [
      {
        "storePlatform": "Apple",
        "productId": "com.yourbrand.app.weekly",
        "displayName": "Weekly access",
        "billingPeriod": "Weekly",
        "isActive": true,
        "storeSharedSecretConfigured": false
      },
      {
        "storePlatform": "Apple",
        "productId": "com.yourbrand.app.monthly",
        "displayName": "Monthly access",
        "billingPeriod": "Monthly",
        "isActive": true,
        "storeSharedSecretConfigured": false
      },
      {
        "storePlatform": "Apple",
        "productId": "com.yourbrand.app.yearly",
        "displayName": "Yearly access",
        "billingPeriod": "Yearly",
        "isActive": true,
        "storeSharedSecretConfigured": false
      },
      {
        "storePlatform": "Google",
        "productId": "com.yourbrand.app.monthly",
        "displayName": "Monthly access",
        "billingPeriod": "Monthly",
        "isActive": true,
        "storeSharedSecretConfigured": false
      }
    ],
    "vodPpv": {
      "isEnabled": true,
      "accessDurationHours": 48,
      "storeProducts": [
        {
          "storePlatform": "Apple",
          "productId": "com.yourbrand.app.vod",
          "displayName": "Unlock VOD",
          "billingPeriod": "OneTime",
          "isActive": true,
          "storeSharedSecretConfigured": false
        },
        {
          "storePlatform": "Google",
          "productId": "com.yourbrand.app.vod",
          "displayName": "Unlock VOD",
          "billingPeriod": "OneTime",
          "isActive": true,
          "storeSharedSecretConfigured": false
        }
      ]
    },
    "mobile": {
      "appSlug": "my-network",
      "logoUrl": "/api/v1/services/custom/users/123/apps/mobile/assets/logo?v=638500000000000000",
      "logoBlobKey": "db:logo",
      "iconUrl": "/api/v1/services/custom/users/123/apps/mobile/assets/icon?v=638500000000000000",
      "iconBlobKey": "db:icon",
      "selectionColor": "FF5722",
      "headerFooterColor": "212121",
      "backgroundColor": "000000",
      "fontColor": "FFFFFF",
      "about": "Welcome to my channel network.",
      "copyrightPolicyLink": null,
      "gdprLink": null,
      "supportEmail": "support@example.com",
      "faqLink": null,
      "termsLink": null,
      "privacyPolicyLink": "https://example.com/privacy",
      "instagram": null,
      "facebook": null,
      "twitter": null
    },
    "roku": null,
    "fireTv": null,
    "appleTv": null,
    "androidTv": null
  }
}

Only the platform-specific object is populated (mobile, roku, fireTv, appleTv, or androidTv). The others are null.

storeProducts lists every paid subscription SKU for the app (weekly / monthly / yearly per store). Multiple rows per store are allowed. vodPpv remains one one-time SKU per store for VOD unlocks. Prices are configured in App Store Connect / Google Play — Strimm stores Product IDs only.

Channels

GET /channels

Lists all channels owned by the authenticated user, each including its video library and any platform-specific VAST ad settings that have been configured.

Example response

{
  "success": true,
  "data": [
    {
      "channelTubeId": 42,
      "name": "Main Channel",
      "url": "main-channel",
      "categoryName": "Entertainment",
      "pictureUrl": "https://cdn.example.com/tubestrimm/123/channel.jpg",
      "description": "My flagship channel.",
      "order": 1,
      "videos": [
        {
          "videoTubeId": 1001,
          "title": "Intro to the Network",
          "description": "A quick overview.",
          "durationSeconds": 185.5,
          "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
          "providerVideoId": "dQw4w9WgXcQ",
          "providerName": "YouTube",
          "categoryName": "Promo"
        },
        {
          "videoTubeId": 1002,
          "title": "Behind the Scenes",
          "description": null,
          "durationSeconds": 612,
          "thumbnailUrl": "https://cdn.example.com/tubestrimm/123/thumb.jpg",
          "providerVideoId": "abc123",
          "providerName": "Custom",
          "categoryName": "Documentary"
        }
      ],
      "vastSettings": [
        {
          "platform": "web",
          "vastUrl": "https://ads.example.com/vast?channel=42&platform=web",
          "vastVersion": 1,
          "skippable": true,
          "adFrequency": 1,
          "midRollFrequency": 10,
          "slottable": false,
          "timeFrom": null,
          "timeTo": null,
          "vastType": "fullscreen",
          "allowAdsInVod": false
        },
        {
          "platform": "roku",
          "vastUrl": "https://ads.example.com/vast?channel=42&platform=roku",
          "vastVersion": 1,
          "skippable": false,
          "adFrequency": 1,
          "midRollFrequency": 0,
          "slottable": true,
          "timeFrom": 18,
          "timeTo": 23,
          "vastType": "fullscreen",
          "allowAdsInVod": false
        }
      ]
    },
    {
      "channelTubeId": 58,
      "name": "Kids Corner",
      "url": "kids-corner",
      "categoryName": "Kids",
      "pictureUrl": null,
      "description": null,
      "order": 2,
      "videos": [],
      "vastSettings": []
    }
  ]
}
GET /channels/{channelId}

Returns a single channel by numeric id, including videos and any configured platform-specific VAST ad settings.

Example response

{
  "success": true,
  "data": {
    "channelTubeId": 42,
    "name": "Main Channel",
    "url": "main-channel",
    "categoryName": "Entertainment",
    "pictureUrl": "https://cdn.example.com/tubestrimm/123/channel.jpg",
    "description": "My flagship channel.",
    "order": 1,
    "videos": [
      {
        "videoTubeId": 1001,
        "title": "Intro to the Network",
        "description": "A quick overview.",
        "durationSeconds": 185.5,
        "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
        "providerVideoId": "dQw4w9WgXcQ",
        "providerName": "YouTube",
        "categoryName": "Promo"
      }
    ],
    "vastSettings": [
      {
        "platform": "web",
        "vastUrl": "https://ads.example.com/vast?channel=42&platform=web",
        "vastVersion": 1,
        "skippable": true,
        "adFrequency": 1,
        "midRollFrequency": 10,
        "slottable": false,
        "timeFrom": null,
        "timeTo": null,
        "vastType": "fullscreen",
        "allowAdsInVod": false
      }
    ]
  }
}

VAST settings object

vastSettings is an array containing one entry per platform that has a VAST tag URL configured in the studio. Platforms without ads configured are omitted, so an empty array means no platform-specific ads are set for the channel.

Field Type Description
platformstringTarget platform: web, roku, mobile, firetv, appletv, or androidtv.
vastUrlstringThe VAST ad tag URL used for this platform.
vastVersionintegerInternal VAST configuration version.
skippablebooleanWhether ads can be skipped.
adFrequencyinteger | nullPre-roll frequency (play an ad every N items).
midRollFrequencyinteger | nullMid-roll frequency as a percentage interval; 0 disables mid-rolls.
slottablebooleanWhether ads are restricted to a daily time window.
timeFrominteger | nullStart hour (0–23) of the ad time window when slottable is true.
timeTointeger | nullEnd hour (0–23) of the ad time window when slottable is true.
vastTypestring | nullAd presentation: fullscreen or picInPic.
allowAdsInVodbooleanWhen true, VAST ads may also run during VOD (on-demand) playback for this platform.

Schedules

GET /channels/{channelId}/schedules?date=yyyy-MM-dd

Returns schedule blocks for a channel on the given date, including ordered videos with playback times. Both regular scheduled videos and live streams linked to the channel are included. Each video has isLive: false for regular schedule entries and true for live streams. Live streams appear as single-video blocks sorted with regular blocks by start time; their channelScheduleId is the negative of the live video id.

Example request

GET /api/v1/rest/channels/42/schedules?date=2026-05-29
Authorization: Bearer {authToken}

Example response

{
  "success": true,
  "data": {
    "channelTubeId": 42,
    "date": "2026-05-29",
    "schedules": [
      {
        "channelScheduleId": 901,
        "startTime": "2026-05-29T06:00:00",
        "published": true,
        "loopDaily": false,
        "videos": [
          {
            "videoTubeId": 1001,
            "playbackOrderNumber": 1,
            "title": "Morning Show",
            "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
            "playTimeLabel": "06:00 AM - 06:30 AM",
            "playbackStartTime": "2026-05-29T06:00:00",
            "playbackEndTime": "2026-05-29T06:30:00",
            "providerVideoId": "dQw4w9WgXcQ",
            "providerName": "YouTube",
            "isLive": false
          },
          {
            "videoTubeId": 1002,
            "playbackOrderNumber": 2,
            "title": "Behind the Scenes",
            "thumbnailUrl": "https://cdn.example.com/tubestrimm/123/thumb.jpg",
            "playTimeLabel": "06:30 AM - 07:10 AM",
            "playbackStartTime": "2026-05-29T06:30:00",
            "playbackEndTime": "2026-05-29T07:10:00",
            "providerVideoId": "abc123",
            "providerName": "Custom",
            "isLive": false
          }
        ]
      },
      {
        "channelScheduleId": -5501,
        "startTime": "2026-05-29T14:00:00",
        "published": true,
        "loopDaily": false,
        "videos": [
          {
            "videoTubeId": 5501,
            "playbackOrderNumber": 1,
            "title": "Live Town Hall",
            "thumbnailUrl": "https://i.ytimg.com/vi/liveExample/hqdefault.jpg",
            "playTimeLabel": "02:00 PM - 04:00 PM",
            "playbackStartTime": "2026-05-29T14:00:00",
            "playbackEndTime": "2026-05-29T16:00:00",
            "providerVideoId": "liveExample",
            "providerName": "YouTube",
            "isLive": true
          }
        ]
      }
    ],
    "totalVideos": 3,
    "totalDurationLabel": "3h 10m"
  }
}

Schedule times are returned in the channel's local timezone. When no blocks exist for the date, schedules is an empty array and totalVideos is 0. totalVideos and totalDurationLabel include both regular and live entries.

Favorites

Manage the authenticated user's favorite channels. Favorites are stored server-side and scoped to the current user (the account identified by the Bearer token), so the same list is available across every device and integration. All endpoints require the Authorization: Bearer {authToken} header.

GET /favorites

Lists the channels the current user has favorited, most recently favorited first.

Example request

GET /api/v1/rest/favorites
Authorization: Bearer {authToken}

Example response

{
  "success": true,
  "data": [
    {
      "channelTubeId": 42,
      "name": "Main Channel",
      "url": "main-channel",
      "categoryName": "Entertainment",
      "pictureUrl": "https://cdn.example.com/tubestrimm/123/channel.jpg",
      "description": "My flagship channel.",
      "favoritedAt": "2026-06-23T09:15:00Z"
    },
    {
      "channelTubeId": 58,
      "name": "Kids Corner",
      "url": "kids-corner",
      "categoryName": "Kids",
      "pictureUrl": null,
      "description": null,
      "favoritedAt": "2026-06-20T18:02:11Z"
    }
  ]
}

When the user has no favorites, data is an empty array.

POST /favorites/{channelId}

Adds a channel to the current user's favorites. The call is idempotent — favoriting a channel that is already a favorite still returns success. Returns channel_not_found when the channel id does not exist or has been deleted.

Example request

POST /api/v1/rest/favorites/42
Authorization: Bearer {authToken}

Example response

{
  "success": true,
  "data": {
    "channelTubeId": 42,
    "isFavorite": true
  }
}
DELETE /favorites/{channelId}

Removes a channel from the current user's favorites. The call is idempotent — removing a channel that is not a favorite still returns success. Returns channel_not_found when the channel id does not exist or has been deleted.

Example request

DELETE /api/v1/rest/favorites/42
Authorization: Bearer {authToken}

Example response

{
  "success": true,
  "data": {
    "channelTubeId": 42,
    "isFavorite": false
  }
}

REST API error codes

  • unauthorized — missing or invalid Bearer token
  • invalid_credentials — login failed
  • account_locked — account is locked
  • account_not_activated — email not confirmed
  • app_not_found — no app configured for the platform
  • channel_not_found — channel id not found or not owned by user
  • unsupported_platform — unknown app platform

Custom Services API (mobile)

Legacy-compatible API used by the Strimm mobile app template (api/v1/services/custom). Responses wrap payloads in { data: ... } without a success flag. Auth tokens are the same GUID Bearer tokens stored in strimm.AuthToken.

Base URL

https://newlook2.strimm.com/api/v1/services/custom

Response envelope

Successful responses:

{
  "data": { ... }
}

Validation errors (HTTP 422):

{
  "data": {
    "message": "The given data was invalid.",
    "errors": { ... }
  }
}

Not found (HTTP 404):

{
  "data": {
    "message": "User is required",
    "status_code": 404
  }
}

Common headers

  • Authorization: Bearer {access_token} — required on protected auth routes
  • Platform-Id: ios / android — preferred client platform for app settings, store SKU selection, and entitlements (falls back to Platform)
  • Platform: mobile — scopes channel lists to the user's mobile app configuration
  • Device-Id — device id for subscription / VOD PPV entitlements (falls back to Accept-Platform-Device-Id)
  • timezone — IANA or Windows timezone id for schedule-day filtering (e.g. America/New_York)
  • Accept-Platform-Device-Id — legacy device id for password-unlock flows

Paid apps and VOD PPV may return HTTP 403 with { "data": { "message": "subscription_required", "status_code": 403 } } on channel/schedule/VOD list endpoints when the viewer is not entitled.

Custom API — Authentication

POST /auth/login

No auth required. Accepts email (or username) and password.

{
  "email": "creator@example.com",
  "password": "your_password"
}
{
  "data": {
    "access_token": "00000000-0000-0000-0000-000000000000",
    "token_type": "bearer",
    "expires_in": 3600
  }
}
POST /auth/refresh

Bearer required. Revokes the current token and returns a new one.

GET /auth/me

Bearer required.

{
  "data": {
    "id": 123,
    "full_name": "Jane Creator",
    "public_name": "creator",
    "publicUrl": "creator",
    "email": "creator@example.com"
  }
}
POST /auth/logout

Bearer required. Revokes the token.

{
  "data": { "message": "Success" }
}

Custom API — User & branding

Path parameter {id} is the account owner's numeric user id or publicUrl slug (same value as the mobile app USER_ID env var).

GET /users/{id}/settings

White-label web/app branding from UserCustomApps, or mobile app colors when configured.

{
  "data": {
    "user_id": 123,
    "title": "My Network",
    "hex1": "#EF4E32",
    "hex2": "#091221",
    "hex3": "#102944",
    "fontColor": "#EAE9F9",
    "header_background": "white",
    "FAQ": "...",
    "PrivacyPolicy": "https://example.com/privacy",
    "logo_header": "{base64}",
    "logo_android": "{base64}",
    "useVastSettings": false
  }
}
GET /users/{id}/app_settings

Mobile (or Fire TV) app record from Studio → Apps. Send Platform-Id: ios or android (or Platform: mobile) so store product IDs resolve for the correct store.

{
  "data": {
    "user_id": 123,
    "slug": "my-network",
    "app_name": "My Network App",
    "hex1": "#FF5722",
    "hex2": "#212121",
    "hex3": "#000000",
    "fontColor": "#FFFFFF",
    "Logo": "{base64}",
    "Icon": "{base64}",
    "privacy_policy_link": "https://example.com/privacy",
    "about": "Welcome.",
    "platform": "Mobile",
    "app_id": "7",
    "is_paid": true,
    "store_product_id": "com.yourbrand.app.weekly",
    "store_products": [
      {
        "product_id": "com.yourbrand.app.weekly",
        "billing_period": "Weekly",
        "display_name": "Weekly access"
      },
      {
        "product_id": "com.yourbrand.app.monthly",
        "billing_period": "Monthly",
        "display_name": "Monthly access"
      },
      {
        "product_id": "com.yourbrand.app.yearly",
        "billing_period": "Yearly",
        "display_name": "Yearly access"
      }
    ],
    "vod_ppv_enabled": true,
    "vod_ppv_store_product_id": "com.yourbrand.app.vod",
    "vod_ppv_access_hours": 48
  }
}

is_paid gates app-wide access via the subscription endpoints. store_products lists every configured subscription for this client store; store_product_id is the primary/first id (compat). When vod_ppv_enabled is true, each VOD video requires a separate one-time purchase (vod_ppv_store_product_id) that unlocks that video for vod_ppv_access_hours.

GET /users/{id}/categories?default=1

Channel categories for filters. Pass default=1 to omit nested channels.

GET /users/{id}/languages

Languages available for channel filtering.

Custom API — Channels & playback

GET /users/{id}/channels

Channels scheduled for today. Send Platform: mobile to limit to channels assigned in the mobile app. Query params: language_id, category_id (comma-separated; 999 = live only), search, skip, take, newest, oldest, most_subscribed.

GET /api/v1/services/custom/users/creator/channels?skip=0&take=20
Platform: mobile
timezone: America/New_York
{
  "data": [
    {
      "id": 42,
      "categoryId": 3,
      "userId": 123,
      "name": "Main Channel",
      "url": "main-channel",
      "pictureUrl": "https://cdn.example.com/.../channel.jpg",
      "isWhiteLabeled": 1,
      "playLiveFirst": false,
      "privateChannel": false,
      "isVerified": true,
      "liveAvailable": true,
      "guideMode": "fixed",
      "keepGuideOpened": 0,
      "vodAvailable": true,
      "scheduleAvailable": true
    }
  ]
}

vodAvailable is true when the channel has at least one linked VOD catalog video. scheduleAvailable is true when the channel has a published linear schedule for today (request timezone). Apps use these flags to show a VOD mode toggle and to keep VOD-only channels in the list even when they have no schedule for today.

GET /users/{id}/channels/video

Scheduled videos for one or more channels and dates. Required query: channels_ids (comma-separated), dates (yyyy-MM-dd, comma-separated). Optional: page, per_page, timezone.

GET /api/v1/services/custom/users/creator/channels/video?channels_ids=42,58&dates=2026-06-04&page=1&per_page=6
{
  "data": [
    {
      "customId": "42-1001-1",
      "videoTubeId": 1001,
      "userId": 123,
      "title": "Morning Show",
      "providerVideoId": "dQw4w9WgXcQ",
      "channelId": 42,
      "duration": 1800,
      "timeDuration": "0h. 30min",
      "startDate": "2026-06-04 06:00:00",
      "endDate": "2026-06-04 06:30:00",
      "providerName": "YouTube",
      "durationInDate": "6:00am-6:30am",
      "thumbnail": "https://i.ytimg.com/vi/.../hqdefault.jpg",
      "streamUrl": null,
      "isVod": false
    }
  ],
  "links": { "first": null, "last": null, "prev": null, "next": null },
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 6,
    "total": 1
  }
}

Note: this endpoint returns the paginated object at the top level (data / links / meta), not wrapped in an extra { data } envelope. isVod is always false for schedule rows.

GET /users/{id}/channels/vod

On-demand (VOD) catalog for one or more channels. Same paginated shape as /users/{id}/channels/video. Required query: channels_ids (comma-separated). Optional: page, per_page (default 50).

GET /api/v1/services/custom/users/creator/channels/vod?channels_ids=42&page=1&per_page=50
Platform-Id: ios
Device-Id: device-unique-id
{
  "data": [
    {
      "customId": "42-2001-vod",
      "videoTubeId": 2001,
      "userId": 123,
      "title": "Highlight Reel",
      "providerVideoId": "987654321",
      "channelId": 42,
      "duration": 720,
      "timeDuration": "0h. 12min",
      "startDate": "2026-06-04 00:00:00",
      "endDate": "2026-06-04 00:12:00",
      "providerName": "Vimeo",
      "durationInDate": "0:00-0:12",
      "thumbnail": "https://cdn.example.com/.../thumb.jpg",
      "streamUrl": "https://player.vimeo.com/...",
      "isVod": true
    }
  ],
  "links": { "first": null, "last": null, "prev": null, "next": null },
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 50,
    "total": 1
  }
}

Apps should render timeDuration (not schedule start/end) when isVod is true. When app VOD PPV is enabled, unlock each videoTubeId via the VOD PPV endpoints before playback.

POST /getChannelToken

Unlock a password-protected channel.

{
  "channel_id": 42,
  "password": "channel-secret"
}

With Accept-Platform-Device-Id, returns { "data": { "message": "Success" } }. With Bearer auth only, returns a fresh token payload (same shape as login).

Custom API — Video tokens, live & favorites

GET /token_by_video/{videoTubeId}

Returns cached Vimeo progressive playback renditions for a video. Used by TV/app clients to pick the best width (1280 / 960 / 640 / 426) for direct playback. Non-expired renditions are returned when available; otherwise every cached rendition is returned so the client can still attempt playback.

GET /api/v1/services/custom/token_by_video/1001
{
  "data": [
    {
      "url": "https://vod-progressive.akamaized.net/.../video-1280.mp4",
      "expiration_date": "2026-06-08T18:30:00Z",
      "width": "1280",
      "height": "720"
    },
    {
      "url": "https://vod-progressive.akamaized.net/.../video-640.mp4",
      "expiration_date": "2026-06-08T18:30:00Z",
      "width": "640",
      "height": "360"
    }
  ]
}

Returns { "data": [] } when no cached renditions exist for the video (e.g. non-Vimeo providers, which are played directly via providerVideoId).

GET /users/{id}/channels/{channelId}/live?date=yyyy-MM-dd

Live-stream videos scheduled on a channel for the given day. When date is omitted the current UTC date is used. Times are formatted yyyy-MM-dd HH:mm:ss.

GET /api/v1/services/custom/users/creator/channels/42/live?date=2026-06-08
{
  "data": [
    {
      "videoTubeId": 5001,
      "channelId": "42",
      "startDate": "2026-06-08 19:00:00",
      "endDate": "2026-06-08 20:00:00",
      "description": "Live keynote",
      "timeDuration": "1h. 0min",
      "isPrivate": false,
      "IsRRated": false,
      "isRemovedByProvider": false,
      "isRestrictedByProvider": false,
      "providerVideoId": "987654321",
      "thumbnail": "https://cdn.example.com/.../live.jpg",
      "title": "Evening Live",
      "uri": "",
      "providerName": "Vimeo",
      "live": true
    }
  ]
}
GET /users/{id}/app/favorites

Lists the channels the user has favorited, most recently favorited first. Each item uses the same channel shape as /users/{id}/channels with marked set to true.

GET /api/v1/services/custom/users/creator/app/favorites
{
  "data": [
    {
      "id": 42,
      "name": "Creator Channel",
      "url": "creator-channel",
      "pictureUrl": "https://cdn.example.com/.../channel.jpg",
      "liveAvailable": true,
      "marked": true
    }
  ]
}

When the user has no favorites, data is an empty array.

POST /users/{id}/channels/{channelId}/app/favorite/add
POST /users/{id}/channels/{channelId}/app/favorite/remove

Adds or removes a channel from the user's favorites. The server validates that the channel exists and belongs to the account, persists the change server-side, then returns success (HTTP 200). Both calls are idempotent — re-adding an existing favorite or removing one that is not favorited still returns success.

{
  "data": { "message": "Success" }
}

Returns 404 with { "data": { "message": "Channel not found", "status_code": 404 } } when the channel is missing or not owned by the account.

GET /channels/{channelId}/vast?platform=mobile

Platform-specific VAST settings for a channel. Prefer Platform-Id header; optional platform query overrides it. Response uses PascalCase keys for legacy TV clients.

{
  "data": {
    "ChannelId": 42,
    "VastVersion": 1,
    "VastUrl": "https://ads.example.com/vast?channel=42",
    "Skippable": false,
    "AdFrequency": 1,
    "MidRollFrequency": 10,
    "Slottable": false,
    "TimeFrom": null,
    "TimeTo": null,
    "VastType": "fullscreen",
    "Platform": "mobile",
    "AllowAdsInVod": false
  }
}

Custom API — Subscriptions & VOD PPV

Native store purchases for white-label apps configured in Studio → Apps. Send Platform-Id (ios / android) and Device-Id on every call. store_platform in request bodies is Apple or Google.

GET /users/{id}/apps/subscription/status

Whether the device/viewer is entitled to a paid app. When the app is not marked paid, entitled is true and isPaidApp is false.

{
  "data": {
    "entitled": true,
    "status": "active",
    "expiresUtc": "2026-09-01T12:00:00Z",
    "isPaidApp": true,
    "storeProductId": "com.yourbrand.app.weekly",
    "storeProductIds": [
      "com.yourbrand.app.weekly",
      "com.yourbrand.app.monthly",
      "com.yourbrand.app.yearly"
    ]
  }
}
POST /users/{id}/apps/subscription/verify

Verifies a store subscription receipt / purchase token and records entitlement for the device (and viewer when authenticated). product_id must be one of the configured subscription Product IDs for that store (any billing period).

{
  "store_platform": "Apple",
  "product_id": "com.yourbrand.app.monthly",
  "receipt_data": "{transaction-receipt-or-purchase-token}",
  "original_transaction_id": "1000000123456789",
  "device_id": "device-unique-id"
}
{
  "data": {
    "ok": true,
    "entitled": true,
    "error": null,
    "expiresUtc": "2026-09-01T12:00:00Z"
  }
}
POST /users/{id}/apps/subscription/restore

Restores entitlement from known original transaction ids (and/or device history). Same result shape as verify.

{
  "store_platform": "Apple",
  "device_id": "device-unique-id",
  "original_transaction_ids": ["1000000123456789"]
}
GET /users/{id}/apps/vod_ppv/status?video_tube_id={videoTubeId}

Per-video unlock status for app-level VOD pay-per-view. When VOD PPV is disabled for the app, unlocked is true and vodPpvEnabled is false.

GET /api/v1/services/custom/users/creator/apps/vod_ppv/status?video_tube_id=2001
Platform-Id: ios
Device-Id: device-unique-id
{
  "data": {
    "vodPpvEnabled": true,
    "unlocked": false,
    "expiresUtc": null,
    "storeProductId": "com.yourbrand.app.vod",
    "accessDurationHours": 48
  }
}
POST /users/{id}/apps/vod_ppv/verify

Verifies a one-time store purchase and unlocks a single video_tube_id for accessDurationHours. Clients should finish the store transaction as consumable so the same Product ID can unlock another video later.

{
  "store_platform": "Google",
  "product_id": "com.yourbrand.app.vod",
  "receipt_data": "{purchase-token}",
  "original_transaction_id": "GPA.1234-5678",
  "device_id": "device-unique-id",
  "video_tube_id": 2001
}
{
  "data": {
    "ok": true,
    "unlocked": true,
    "error": null,
    "expiresUtc": "2026-06-06T18:00:00Z"
  }
}

Validation failures return HTTP 422 with { "data": { "message": "The given data was invalid.", "errors": { "vod_ppv": "..." } } }.

Custom API — Analytics

Activity ingestion for the white-label TV and mobile apps. Replaces the retired analytics-api.strimm.com service: apps report watch heartbeats and login/logout events directly to this host.

POST /activity

No auth required. Accepts application/json or application/x-www-form-urlencoded bodies. Also reachable at the legacy-compatible path https://newlook2.strimm.com/api/v1/activity (same handler) for clients that kept the old URL shape and only swap the host.

Request body

{
  "source": "android_tv",
  "action": "play_channel",
  "client_id": "123",
  "channel_id": "42",
  "user_id": "device-id-or-viewer-id"
}

Fields

Field Type Description
sourcestringRequired. One of roku_app, fire_tv, android_tv, apple_tv, mobile_app_on_ios, mobile_app_on_android, custom_app_on_ios, custom_app_on_android, website_embedded_player, broadcast_on_strimm_dot_com.
actionstringRequired. play_channel, login, or logout (other legacy action names are accepted but not processed).
client_idinteger | stringStrimm account owner user id (the app's USER_ID).
user_idstringEnd-viewer/device identifier (e.g. Android ID, identifierForVendor, Roku tracking id). Required for login/logout.
channel_idinteger | stringChannel being played. Required for play_channel.
payloadany | nullOptional extra data, stored as-is.

Response (HTTP 200)

{
  "message": "Success"
}

Note: for compatibility with the legacy analytics contract this endpoint returns a bare { "message" } object, not the { data } envelope used by the rest of the Custom API.

Watch-session heartbeats

While a channel is playing, post a play_channel event every 60 seconds. The server folds consecutive heartbeats with the same source + client_id + user_id + channel_id into one watch session, extending its end time on each heartbeat. If no heartbeat arrives for more than 3 minutes the session is closed and its duration recorded (minimum 60 seconds credited); the next heartbeat starts a new session.

Errors

  • 403 invalid_user — the owner of channel_id has no qualifying subscription order
  • 422 — validation failed (unknown source/action, missing required field, or unparsable body)

Custom API notes

  • Roku, Fire TV, and other platform-specific routes under /api/v1/services/{platform} are planned separately.
  • Favorites (list/add/remove) are persisted server-side per user. Watch-later, search, and social auth endpoints are not yet implemented on this host.
  • Paid subscription and VOD PPV are configured per app in Studio → Apps. Subscription is app-wide; VOD PPV unlocks are per videoTubeId + device (or authenticated viewer).
  • Archive builds bake IS_PAID, STORE_PRODUCT_ID_*, VOD_PPV_ENABLED, VOD_PPV_STORE_PRODUCT_ID_*, and VOD_PPV_ACCESS_HOURS into the mobile template .env.
  • Configure the mobile app API_BASE_URL to this site's origin (e.g. https://newlook2.strimm.com).

Legacy Web Services (ASMX-compatible)

Read-only compatibility endpoints that mirror the old *.asmx web services, kept for shipped Roku / mobile / TV apps that still call the original URLs. Paths, parameters and response bytes are identical to the legacy site — new integrations should use the REST API above instead.

Base URL

https://newlook2.strimm.com/WebServices

Conventions

  • No authentication. Every endpoint accepts GET (query string) and POST (application/x-www-form-urlencoded body).
  • Except for GetVastSettings (raw JSON body), responses are JSON wrapped in the classic ASMX XML envelope, served as text/xml; charset=utf-8:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://tempuri.org/">{ ...json... }</string>
  • Every declared parameter must be present on the wire (an empty value counts); omitting one returns the legacy HTTP 500 Missing parameter plain-text page.
  • JSON keys are PascalCase and lookups that find no row serialize as the literal null.

Legacy — VAST settings

GET POST /VastService.asmx/GetVastSettings?channelId={channelId}

VAST ad settings for a channel. Unlike the other legacy endpoints, the response is a raw JSON body (no XML envelope, no Content-Type header). When the channel has per-platform rows configured, the web row is returned.

Example response

{
  "ChannelId": 42,
  "VastVersion": 1,
  "VastUrl": "https://ads.example.com/vast?channel=42",
  "Skippable": false,
  "AdFrequency": 1,
  "MidRollFrequency": 10,
  "Slottable": false,
  "TimeFrom": null,
  "TimeTo": null,
  "VastType": "fullscreen",
  "Platform": "web",
  "AllowAdsInVod": false
}

Returns the literal null when the channel has no VAST settings.

Legacy — Roku app

GET POST /RokuAppGeneratorService.asmx/GetChannelTubeRokuSettings?channelId={channelId}

Whether a channel is included in the account's Roku app.

Example response (JSON inside the XML envelope)

{
  "ChannelTubeId": 42,
  "AddedToRoku": true,
  "LastUpdateDate": "2024-01-22T12:56:28"
}
GET POST /RokuAppGeneratorService.asmx/GetUserRokuApp?userId={userId}

The account owner's Roku app record. Images are base64-encoded PNG bytes.

Example response (JSON inside the XML envelope)

{
  "UserID": 123,
  "AppName": "My Network",
  "AdLink": "https://ads.example.com/roku",
  "About": "Welcome to my channel network.",
  "PrivacyPolicyLink": "https://example.com/privacy",
  "ImageHD": "{base64}",
  "ImageSD": "{base64}",
  "ApiKey": "00000000-0000-0000-0000-000000000000"
}

Legacy — Mobile app

GET POST /MobileAppGeneratorService.asmx/GetChannelMobileApp?userId={userId}&channelId={channelId}

The account owner's mobile app branding plus the channel's app membership (Sorting, AddedToApp, UseVastSettings come from the channel's app assignment; they are null / account-level when the channel is not assigned).

Example response (JSON inside the XML envelope)

{
  "UserID": 123,
  "ChannelTubeId": 42,
  "HeaderTitle": "My Network",
  "Slug": "my-network",
  "HeaderLogo": "{base64}",
  "LogoIOS": "{base64}",
  "LogoAndroid": null,
  "Hex1": "FF5722",
  "Hex2": "212121",
  "Hex3": "000000",
  "FontColor": "FFFFFF",
  "About": "Welcome.",
  "Sorting": 1,
  "CopyrightLink": "",
  "GDPRLink": "",
  "CustomerSupportEmail": "support@example.com",
  "FAQ": "",
  "TermsLink": "",
  "PrivacyPolicyLink": "https://example.com/privacy",
  "Instagram": "",
  "Facebook": "",
  "Twitter": "",
  "AddedToApp": true,
  "UseVastSettings": false
}

When userId or channelId is sent empty, the envelope contains the plain string One of the required fields is not passed.

Legacy — TV app

GET POST /TVAppGeneratorService.asmx/GetUserTVApp?userId={userId}&platform={platform}&skipImages={true|false}

The account owner's TV app (theme, images, and per-channel settings) for a platform: fire-tv, android-tv, or apple-tv. With skipImages=true the binary fields (Logo, Icon, BackgroundImage, Image1, Image2) and AppId are returned as null. Each entry in Settings includes the channel's VAST configuration when UseVastSettings is enabled.

Example response (JSON inside the XML envelope)

{
  "UserID": 123,
  "AppName": "My Network TV",
  "Slug": "my-network-tv",
  "About": "Welcome.",
  "PrivacyPolicyLink": "https://example.com/privacy",
  "Logo": null,
  "Icon": null,
  "BackgroundImage": null,
  "Image1": null,
  "Image2": null,
  "PrimaryColor": "FF5722",
  "SecondaryColor": "212121",
  "BackgroundColor": "000000",
  "StyleColor": "102944",
  "Platform": "fire-tv",
  "ChannelId": null,
  "AppId": null,
  "Settings": [
    {
      "ChannelTubeId": 42,
      "AddedToTV": true,
      "Platform": "fire-tv",
      "UseVastSettings": true,
      "ChannelNumber": "1",
      "AppId": null,
      "LastUpdateDate": "2026-05-29T10:15:00",
      "VastSettings": {
        "ChannelId": 42,
        "VastVersion": 1,
        "VastUrl": "https://ads.example.com/vast?channel=42",
        "Skippable": false,
        "AdFrequency": 1,
        "MidRollFrequency": 10,
        "Slottable": false,
        "TimeFrom": null,
        "TimeTo": null,
        "VastType": "fullscreen",
        "Platform": "fire-tv",
        "AllowAdsInVod": false
      }
    }
  ]
}

Errors (HTTP 200, JSON inside the XML envelope)

  • {"error":"One of the required fields is not passed"}userId or platform sent empty
  • {"error":"Application does not exist on {platform} platform"} — no app configured for the user on that platform